Tracing Gate::define0:27
you get good ideas, inspiration, things of that nature. Okay, so with that in mind, if you're curious, why don't we dig in and figure out what's happening here? I think we should start, if I go to my AuthServiceProvider, by that first example we used, where we said, Gate::define something like an ability, and then we had a closure here that accepts the authenticated $user, a $post, and then we have our logic here. And in this case, we just said, $user owns $post. Okay, so, well, I guess step one is, what happens when we call a define method?
And in this case, we just said User owns Post. Okay, so, well, I guess step one is, what happens when we call a define method? All right, let's try it out. I'm going to switch to the Gate class and look for a define method. And here's the first step. Now, you'll see that we can set an ability and also a callback. But here's one of the interesting things about Laravel, and you'll see this throughout the code base. In many situations, yes, you can pass a closure, or you can pass a class path, like this.
In many situations, yes, you can pass a closure, or you can pass a class path, like this. So what Laravel ends up doing is, it figures that out for you. Are you referencing a closure here, or are you referencing a string? And if it's a string, we'll parse it, and then we will basically wrap it within a function so that we can call it in exactly the same way. Okay, so let's see. Okay, so we define an ability, and remember, we called this updatePost when we triggered the method.
Okay, so we define an ability, and remember, we called this updatePost when we triggered the method. All right, so we're checking to see, well, can we call our callback? And of course we can, we gave it a closure. So this is what we do in that case. There is an abilities array stored on the Gate class, we give it an identifier, and then we make that equal to our function that determines whether that ability applies to the authenticated user. Okay, so that was easy.
Normalizing String Callbacks2:03
that determines whether that ability applies to the authenticated user. Okay, so that was easy. Now, what if, like I told you earlier, well, we don't reference a closure here, we reference a string that represents a class and a method? Well, if that's the case, Laravel has to normalize everything. So we check. Well, did you give us a string? And then also, did you give us an @ symbol which separates the class name from the method?
And then also, did you give us an @ symbol which separates the class name from the method? If that's the case, well, once again, we will define the ability, but we need to make sure that we parse everything correctly. So that's why we trigger this method. Okay, so let's bring this back to the variable name and figure out how that works. Well, this is what I was talking about when I said that if you pass a closure, Laravel will just trigger it.
when I said that if you pass a closure, Laravel will just trigger it. But if you instead pass a string representing a class and a method, Laravel will wrap that within a closure itself and then ultimately trigger it for you. So that way, no matter what, we're dealing with a closure. And in this case, notice, like if you have class at method, well, we explode that into the class name and the method name, and then we resolve the policy, and all that does is think of it as app() make.
and then we resolve the policy, and all that does is think of it as app make. It just news up the class and applies any dependency resolution. Anyway, so we knew that up, and then we call the method name on the class, and we pass through any of our arguments. But remember, we don't do that until the closure is triggered. All right, so that's how that works. So things are starting to make sense. We figured out how we handle closures as well as string paths.
Gate::denies and check3:33
So things are starting to make sense. We figured out how we handle closures as well as string paths. Finally, we can see that we return this, so that's it. That's all the define method does. It pushes to an abilities array and makes that ability equal to a closure that validates it, essentially. Let's move on to the next step. What about this section right here, where we reference our facade? Well, immediately, if we take a look at the Gate facade, that's just a pointer to the contract.
Well, immediately, if we take a look at the Gate facade, that's just a pointer to the contract. So really what we want is the underlying class, which is called Gate. Okay, so we're calling a denies method on it. Let's see what that does, and I will stretch this out. Okay, denies. Okay, well, it looks like that just returns the opposite of an allows method. And if you remember, I noted in the previous video, you can call Gate::allows or Gate::denies. They are nothing more than opposites.
you can call Gate::allows or Gate::denies. They are nothing more than opposites. Okay, so let's see what's happening here. We call a check method. We pass through the ability, like updatePost, and then we pass through, well, in this case, it would be like the post object. Remember when we say Gate::allows, updatePost, and then you pass that through? Ultimately, that will trigger a check method. Okay, let's check that out. And now we fetch the user.
Okay, let's check that out. And now we fetch the user. So that's just fetching the authenticated user. Next, it looks like we parse this into an array. That's really the same as doing this. So what this tells us is, when we, once again, say Gate::allows, update Post. Well, yeah, you can pass that through. Or if you need to pass through multiple arguments, like Post and Comment, you can just do it as an array, or you can just keep it like this. No matter what, it'll turn into an array of your arguments.
Before Hooks Behavior5:17
you can just do it as an array, or you can just keep it like this. No matter what, it'll turn into an array of your arguments. Okay, so next, we call our before callbacks. What's happening here? Well, this component gives us the ability to hook into right before we perform our checks. And this can be useful in situations where, for example, you want to give the top administrator of your application access to everything. So you don't even want to perform the actual authorization. If you are this user with the ID or a user of this type, you instantly get access to everything.
If you are this user with the ID or a user of this type, you instantly get access to everything. Now, notice the way we check that is, there is an array of before callbacks on this object. But first, before we move ahead, how do we reference that? When does that get set? Well, you'll see there's a before method on the Gate class, where, once again, we just add to an array. So what does this tell us? Even if we didn't read the Laravel documentation, what this tells us is, if I switch back to my AuthServiceProvider,
Even if we didn't read the Laravel documentation, what this tells us is, if I switch back to my AuthServiceProvider, you can add a before hook using a before method and passing through a closure. That will, once again, accept the $user and the $ability. Okay, so now, let's say, for example, if the $user->id equals 1, or if the $user is the administrator, whatever check you need to perform, you can do so. Now, this will return a Boolean. So let's just imagine we are returning true in this case. Okay, well, if we switch back, and once again, I like to go through this over and over. So we're back to that check method, we get the $user, we get the $arguments,
Okay, well, if we switch back, and once again, I like to go through this over and over. So we're back to that check method, we get the user, we get the arguments, and then before we even trigger that closure, we have the option of returning early, or returning before anything else occurs. So we check that out. We now filter through that array that we set, and if we have any items in it, for each one, we will trigger the function. So what we're saying here is, trigger this closure right here and get the results. And now, if what was returned is not null, because remember, in some situations, you may just want to perform some kind of action that doesn't return anything.
And now, if what was returned is not null, because remember, in some situations, you may just want to perform some kind of action that doesn't return anything. If that's the case, fine, no problem there. But if you did return a value, we will assume you're returning a Boolean, which indicates if this person has permission, and we return it right there. Okay, so let's go back, our check method, we call our before callbacks, and if we did get something in response, if it's not null, then we're going to assume you've decided already if this person has authorization, so we immediately return, once again, before we ever trigger our class method here. That's important to know.
Resolving Policy Callbacks8:00
so we immediately return, once again, before we ever trigger our class method here. That's important to know. Okay, but let's imagine, no, we didn't even define a before method. So that doesn't apply. Next, we resolve the auth callback, and we pass in the user, the ability, which is update post in our case, and then the arguments, which would be the post in question. Okay, let's see what's going on here. Resolve auth callback. This is where we need to figure out what we are dealing with.
Resolve auth callback. This is where we need to figure out what we are dealing with. For example, if we go to AuthServiceProvider, yes, we could define an ability like this. But we also learned in the last lesson that we could alternatively define our abilities using dedicated Policy objects. Well, Laravel needs to figure out what we're working with here. So that's what this section does for us. Notice, if the first argument corresponds to the Policy. Remember, it's checking to see the Post, and it's saying, okay, while you pass through Post, does that correspond to a dedicated policy class?
Remember, it's checking to see the Post, and it's saying, okay, while you pass through Post, does that correspond to a dedicated policy class? Let's see. Well, how do we check that? We check to see if we have arguments. So remember, this is what we're dealing with at this point, the Post. Okay, do we have a first item in the array? Yes. Next, is that first item an object? Yes. And then finally, this is the key right here that tells us if we have a corresponding policy object. First, we get the class of the argument.
And then finally, this is the key right here that tells us if we have a corresponding policy object. First, we get the class of the argument. So that would be, for example, App\Post class. And then we check to see, well, do we have that path on our policies array? And if we do, that means the User is referencing a dedicated policy object. And don't forget, here's where we defined that. We defined it right here. So we're just saying, look in this policies array, and do you have any key or lookup for this Post class? And if you do, this is what we need to extract.
and do you have any key or lookup for this Post class? And if you do, this is what we need to extract. And then don't forget, when we boot, we actually assign these to the gate object by calling a policy method. And if we take a look at that, that just pushes to the policies array. Okay, so I know this is kind of hard to take in. Go and do this yourself or go back and watch the video, because I do understand it's kind of hard to follow each of the steps. But hopefully this makes sense. We have a Policy class if what we're dealing with is an object, and also where the getClass of Post, well, do we have a lookup for that within our policies array?
We have a Policy class if what we're dealing with is an object, and also where the getClass of Post, well, do we have a lookup for that within our policies array? And if we do, yep, we're dealing with a Policy class. Okay, so we got that part done. Let's go back. Now, in our case, what will this return? Well, we've done both. You can see I've commented all of this out. So we just have a PostPolicy. So in that case, yes, what we gave to you does have a Policy class associated with it.
So we just have a PostPolicy. So in that case, yes, what we gave to you does have a policy class associated with it. So in that case, we're going to do the exact same thing as we did before, where we normalize everything. So take a look. We return or we wrap it within a closure. And that way, down the line, as I'll show you, it just makes it that much easier to trigger, because no matter what, we're just dealing with a closure that can be called. Okay, so let's see what happens here. We resolve the policy. Once again, all that does is container make.
We resolve the policy. Once again, all that does is container make. It's the same as saying app class. We just new it up. We resolve it out of the service container. Okay, so we now have our instance, our new instance of PostPolicy. Next, we do the exact same thing. We check to see did the User define a before trigger. So here's what I mean by that. Within AuthServiceProvider, yes, we learned that we could do this.
So here's what I mean by that. Within AuthServiceProvider, yes, we learned that we could do this. But in our case, we're using a dedicated policy class. So in that case, we can achieve the exact same end result by creating a before method that once again accepts the $user and the $ability. And of course, any other arguments that you might pass to it. But now, yeah, the exact same thing here. If you want to accept the super administrator, then you could say $user is admin or something like that. Or if it's not applicable, you can leave it off entirely. It's good to know that's an option.
we're going to assume that means you are returning whether or not the User is authorized. So if that's the case, we will return early and never actually trigger the update method. Otherwise, well, we continue on and we call on your PostPolicy instance. We call the ability which was update. Okay, so hopefully this is starting to make sense now. When we say things like Gate denies update on Post, well, we're starting to figure out how all of this works. When we reference update here, if we're dealing with a policy class, we are literally referring to the name of the method that we will trigger. Okay, so let's undo, bring this back to the variables, like so.
we are literally referring to the name of the method that we will trigger. Okay, so let's undo, bring this back to the variables, like so. Okay, so this is looking good. We've resolved our Policy class. We've normalized everything. Now, what about the situations where, no, you're not dealing with a Policy class? For example, we don't have anything here, and instead we just used this format. Okay, well, if we come back, we check to see, well, did you define an ability? Do we have that set? And if so, well, in that case, that would be equal to the given closure.
Do we have that set? And if so, well, in that case, that would be equal to the given closure. And notice, no matter what, we return a closure. That's what I mean when I say we are normalizing everything. Okay, so we go back. We have our closure or our callback. And this will be consistent whether we said Gate::define the ability and then a closure here or if we did Gate::define an ability and then a class and a method or if we used a PostPolicy object instead with an update method. No matter what the case is, we normalize everything into something that is callable.
or if we used a PostPolicy object instead with an update method. No matter what the case is, we normalize everything into something that is callable, and then, well, we call it. And we pass through the $user, and in this case, the $array, which just contains the post. And that's it. So whatever gets returned from that closure determines whether or not the $user is authorized to perform that action. So hopefully now you can see it's actually not that complex, but it's incredibly useful. So now whenever you do something like this, you actually understand what's happening behind the scenes.
End-to-End Authorization Recap14:38
So now whenever you do something like this, you actually understand what's happening behind the scenes. Why don't we go through everything one more time really fast just to confirm that we understand it. Okay, so we're going to use a policy class. So we define one or a lookup right here. Now within that PostPolicy, we have a single method that determines if the User has the ability to update a Post. Next, within our PostController, we have our check here. Are we allowed to update the given Post?
Next, within our PostController, we have our check here. Are we allowed to update the given post? And if not, we abort. Okay, well, we know on the Gate class, we have a denies method that refers to a allows method. It just negates that. And further, we know that ultimately this refers to a master check method. And by the way, we should bring that back to the ability. Okay, now our check method is where we perform most of the logic here. We fetch the user.
Okay, now our check method is where we perform most of the logic here. We fetch the User. We normalize the arguments into an array. Okay, next, we check to see do we have any before callbacks. No, we don't. Next, we normalize everything into a closure. So in this case, if we take a look, yep, that first argument post, well, that actually is contained within a lookup. So we basically said App\Post::class does have a lookup for App\Policies\PostPolicy::class. So because we have a lookup for that, that means the User does want to use
So we basically said App\Post does have a lookup for App\Policies\PostPolicy. So because we have a lookup for that, that means the User does want to use a policy object. So if that's the case, well, we normalize it into a closure here. We new up the PostPolicy class. We once again check to see did the User define a before method on that PostPolicy class. And if so, we'll trigger it and potentially return early. Otherwise, we will call the update method on the postPolicy object. And once again, we pass through the User, and in this case, the Post.
Otherwise, we will call the update method on the PostPolicy object. And once again, we pass through the $user, and in this case, the $post. Now, whatever gets returned from that update method, if I undo a few checks, will determine if the person has access. So in this case, does the $user own the $post that will return true or false? Finally, within our check method right here, we have our closure. We trigger it. We return that Boolean, and that determines if the $user has permission. Finally, back in our PostController, we have our result, true or false, and that's how we decide if we should abort or not.
Finally, back in our PostController, we have our result, true or false, and that's how we decide if we should abort or not. That's all there is to it.
