تماشای این درس نیاز به اشتراک حرفه‌ای دارد.

Auth vs Authorization0:00

So I actually put a pause in the recording schedule I originally made because I got tired of saying, if we had time, we would do more in-depth of a look at authorization, because we do have time and I want to do it. So we're going to take a look at authorization, the different ways we could consider doing authorization in a Laravel app. And the good news is, we're working with pretty simple stuff. Can the user do this action or that action with this particular model item, you know, with this particular post or this particular talk in our context? And so there's a few main ways to do it. We're going to look at them, and we're going to make sure we implement the best option that I think we have for the app we're working on right now. So first, let's talk really quickly about the difference between authentication and authorization. Authentication means you've proven who you are. That's what auth stands for here. It means just the users have to be logged in in order to do these things. They have to be logged in in order to see the dashboard. And in order to all the other things we're doing here, including create talks, view talks, everything else like that. Authorization says, now that we know who you are, we want to make sure that you're allowed to do that thing. And that's when we were building things, for example, the TalkController, we were saying, if talkAuthorID is not equal to the authUserID, then abort(403). And I said, this is a clumsy way to do it. And it's not how we would probably do it in an app. I just wanted to show that our tests were working. But now let's actually talk about the different ways to do it.

FormRequest Authorization1:09

we were saying, if talk authorId is not equal to the auth userId, then abort 403. And I said, this is a clumsy way to do it. And it's not how we would probably do it in an app. I just wanted to show that our tests were working. But now let's actually talk about the different ways to do it. So the first thing is, we can just do it in line like this, compare the authorId against the auth userId. The next thing we could do is a FormRequest. And a FormRequest is the ability to structure together the validation and the authorization for this particular endpoint, usually less around showing, and usually more around creating or editing, it's usually something about a post. So it wouldn't necessarily work here. But let's take a look maybe here on the update route. And so what we would do is we'd make a FormRequest specifically attached to the concept of updating a talk. And in that FormRequest, if we say, php artisan make:request, it would be called UpdateTalkRequest. That would be what we use for this specific method here for this specific route, we say UpdateTalkRequest. One of the things we do in this is authorize whether or not you're allowed to do this.

That would be what we use for this specific method here for this specific route, we say updateTalkRequest. One of the things we do in this is authorize whether or not you're allowed to do this. And so this is where we would check, are you actually the person who's allowed to do this. So we can say, because this is a request, we can say $userId equals $talk. And we're able to do this right here, because we're using route model binding. If we weren't using route model binding, we'd actually have to pull it out of the route based on its segment name. Because we're using route model binding, we can just say $talk->authorId, what we would do in order to make this work is we would move our rules from here into that form request. And then when we're type hinting a request in this method here, we wouldn't ask for a normal Illuminate\Request, we would ask for that specific UpdateTalkRequest. And then it would run those authorize methods for us, and it would also run the validation for us. So in theory, if we move these validation rules from our TalkController into our form request,

And then it would run those authorize methods for us, and it would also run the validation for us. So in theory, if we move these validation rules from our TalkController into our FormRequest, we don't have to validate it here, we can just say talkUpdateRequest validated, and that's going to give us the actual validated data. So in theory, that should work. And it did not. Let's check our authorization to see if there's anything I missed. And I did, I call this authorId when it's actually userId. Let's try it again. And there we go. So our authorization tests are now working and they're being powered no longer by that inline method, but instead by the authorized method here. And look at how clean that controller is. So that's FormRequests.

Gates vs Policies3:52

And look at how clean that controller is. So that's form requests. We looked at the inline manual version, we've looked at form requests, and there's two more options, gate and policies. Laravel's gate is simply the ability to define usually in something like an AppServiceProvider. A ability with just a string name. So you can say it's something like update-talk or something like that. You pass in first the $user trying to define, you know, who's the user that we're asking? Can they do that? And then a thing. So in this particular case, it would be a talk that we're trying to define ability against. So in this circumstance, you would say something like, oops.

So in this particular case, it would be a talk that we're trying to define ability against. So in this circumstance, you would say something like, oops. Thought I'd imported those. So you'd say something like return userId is the same as talkUserId. So this is the ability to just define something very simple in a string type way. And this string could be anything, you know, we have followed a kind of common syntax of verb dash entity, but it doesn't have to be that way. Because gates really allow you to find anything you want. And so if you ever want to say somebody is authorized or not authorized to do something that doesn't specifically have a common behavior against an entity, gates are really great for that. But when you're talking about a common behavior against an entity, which is what we're doing, right? Update a resource of type Talk or whatever, policies are gates with a lot more conventions around it.

Building a TalkPolicy5:14

But when you're talking about a common behavior against an entity, which is what we're doing, right? Update a resource of type talk or whatever, policies are gates with a lot more conventions around it. So you don't have to kind of make it up every time you want. So in this particular circumstance, we're actually going to do policies. Which I think are better. So what we're going to do is we're going to make a policy for talk. Model equals talk. So in our TalkPolicy, it's already specifically attached to the talk model. And you can see we've got all these different methods. viewAny is mainly used in things like Nova.

And you can see we've got all these different methods. viewAny is mainly used in things like Nova. Can they viewAny talks at all? This can determine whether they can view a specific Talk, whether they can create talks at all, which is why it doesn't have a Talk passed into it. Whether it can update a specific Talk, delete a specific Talk, restore a specific soft deleted Talk, or force delete a specific Talk to bypass soft deletes. So the main ones we want to see on this particular app are can I view this Talk? Can I create a Talk? We'll just say everybody can create talks. Can I update this Talk? And can I delete this Talk?

Can I update this talk? And can I delete this talk? So view any, sure. View a specific one. Update a specific one. Delete a specific one. Restore a specific one. Force delete. These are all the same for all of ours. There is a way to do sort of like a default for those, but I often find the defaults don't work exactly the way people want.

These are all the same for all of ours. There is a way to do sort of like a default for those, but I often find the defaults don't work exactly the way people want. So I do something that maybe isn't the right way to do it, but I just like it where I just say private function userOwnsTalk or something like that. And I don't have a perfect kind of syntax for this, but. Let's just say return $userId is the same as $talkUserId. And then we just do that for all of them. And this may feel a little bit clumsy. And like I said, there is this before thing that you could do that just says basically before checking any of the rest of these, if the before returns true for this person, you don't even check the rest. And that is a reasonable way to do things like a TalkPolicy. Sometimes I'll do that.

And that is a reasonable way to do things like a talk policy. Sometimes I'll do that. I'd like to be a little bit more explicit in case I want to differentiate these things at some point. So I'm just going to be a little bit repetitive here, and we're just going to grab the name of our method. User owns talk. User talk. We're just going to do that everywhere where it's about ownership. All right. So we've got a policy. How do we use that policy?

Applying Policies via Routes8:02

So we've got a policy. How do we use that policy? And there's a couple different ways we could use it directly in line. Let's try the show method for now. We could say if lastUser cannot view talk, then do something, you know, throw your report. But that's no better than the in line we had before. So what we really want to use policies for. I mean, it's nice, I guess, because the logic for whether or not somebody can do something is now stored into the policy for use as many times as possible. But it hasn't cleaned up our code at all, which I want as well. We could if we want to just say Gate::authorize.

But it hasn't cleaned up our code at all, which I want as well. We could if we want to just say Gate::authorize. And then the action and then the resource. And if it fails, it's going to throw up an authorization error. But what I prefer to do is to do it directly in the routes file as middleware. And this specific middleware actually has a shortcut, which is can. We can say can view talk. And let's see how our tests turn out. Fantastic. So we're not taking advantage of this policy directly in our route definition files.

Fantastic. So we're not taking advantage of this policy directly in our route definition files. So our controllers have been cleared up because they don't have that extra code. But additionally, not only do they not have that extra code, but that code is being centralized in one place anywhere in our entire application. If we want to check, can somebody view this talk or show this talk? We have a single place where that logic is encapsulated and it can be asked in a controller. It can be asked in any direct code. It can be asked as a middleware. There's so many different ways we can access this information, but it's all pulling from the same single source of truth, which is why policies are wonderful. And Laravel's ACL layer is just one of my favorite little tools.

There's so many different ways we can access this information, but it's all pulling from the same single source of truth, which is why policies are wonderful. And Laravel's ACL layer is just one of my favorite little tools. So hope you learned something.

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟