Note: some of the features discussed below have originaly been introduced in the middle of the 5.2 cycle. However, all of these features have been tweaked and refined for 5.3, so we'll cover them here.
If you're already familiar with authorization in Laravel, you can skip right ahead.
Laravel's authorization is built around 2 main concepts:
A Gate class. The gate is the official authority on who has what ability (i.e. who can do what). You use the gate to register a user's abilities, and later check the gate if the user can perform a given action.
Policy classes. Policies are responsible for checking abilities on a single model type. For each model class in your system you want to authorize against, you'll have a matching policy class.
To see how all this works together, let's generate a policy class for a theoretical Task model, by running php artisan make:policy TaskPolicy. Once created, we'll add a simple update method that checks if the user can update the given task:
We check the gate if the user can update the given task. The gate will call the update method on our policy, passing it both the currently authenticated user as well as the given task (if the user is not logged in, the gate will automatically deny all ability inquiries). If the ability is not granted, we abort with a 403.
(By the way, don't you just love that abort_unless function? I mean, just look at that! It reads like a full sentence: "abort unless the gate allows updating the task". This is Laravel at its best.)
Controllers in Laravel use the AuthorizesRequests trait to easily authorize any given request. You can call the authorize method from within a controller and it'll check for the ability at the gate. If the ability is denied, the request will automatically be aborted with a 403 response.
To see this in action, let's move our route to a controller:
Laravel actually goes a tiny step further, and allows you to omit the ability name when calling the authorize method. When the ability name is not provided, Laravel will automatically pick up the ability name from the controller's method.
In our sample controller, calling $this->authorize($task) would still end up consulting TaskPolicy@update for the check. Pretty nifty!
In Laravel 5.2, the automatic ability name is always the name of the controller method that calls it. Given the following controller:
...you would have a policy with methods whose names match up with the names of the controller's methods:
useApp\Task;
useApp\User;
classTaskPolicy{
publicfunctionshow(User $user, Task $task){
// check if the user can view the given $task
}
publicfunctioncreate(User $user){
// check if the user can create new tasks
}
}
Update August 8, 2016: after publishing this article, Taylor made some additional changes to authorization. The following has been updated to reflect these changes.
On the surface this seems quite intuitive, but if you looks closely you'll find some cracks in this convention.
Consider these 2 samples of where this convention breaks down:
Checking abilities outside of a controller. The controller's method name is not always the most intuitive way to check an ability. Let's examine this template, where we loop through and check each task:
Hmmm... That doesn't look quite right. What does $user->can('show', $task) check for? Does it check if the user can show tasks? What does that even mean? We want to know if they can view it, not show it!?
Duplicate policy methods. The check for whether we should show the form to create a new task is the very same check that determines whether we allow the user to subsequently store that task. Naively mapping the controller's method names leads to the policy having two identical methods:
useApp\User;
classTaskPolicy{
publicfunctioncreate(User $user){
// check if the user can create new tasks
}
publicfunctionstore(User $user){
// check if the user can create new tasks
}
}
These two run the very same check, but we need them both for the two different methods on the controller. The same duplication applies to edit and update.
In Laravel 5.3, the automatic ability names are getting an upgrade. Instead of blindly using the method name, Laravel will pick a sensible ability name based on the action you're trying to complete. If you're trying to view an item, it'll use the view ability name. If you're trying to create a new record, it'll use the create ability name, even if you call it from the controller's store method.
Laravel does this via a simple mapping between the methods and abilities. Here's the complete map straight from the source:
This happens automatically, with no change to the code in your controller. You can now check if $user->can('view', $task) in your views, which is much more intuitive.
Now that the controller doesn't always call the gate with ability names matching the controller methods, the policies should no longer have those methods. Given this controller:
To help out with these changes, the policy generator is getting a nice boost. If you pass a model name to the generator it'll fully stub out the necessary policy methods to cover all default resource controller methods:
php artisan make:policy TaskPolicy --model=Task
You can see the raw stub in Laravel's source code, which should give you a better picture about the methods being generated.
While authorizing abilities inline works well, sometimes it makes more sense to do it at the route level - such as when the same ability applies to a whole group of routes. Laravel ships with a can middleware just for this purpose.
To guard a whole group of routes against unauthorized access, we can wrap them in a group with the can middleware:
Route::group(['middleware' => 'can:access-dashboard'], function(){
// define all dashboard routes...
});
Now the gate will be checked for the access-dashboard ability before running any routes in this group.
You can also pass a second argument to the middleware. This can be either the model's class name, or - to check for a given model instance - the model's route parameter name:
When passing a route parameter name, the middleware will pull the model instance from the route itself. Refer to Laravel's documentation for more on route model binding.
Another neat gem in the authorization system is the authorizeResource method now available in controllers. This has the potential to considerably clean up your controllers. So what does this magical authorizeResource do?
In short, it replaces all your calls to authorize in the various controller methods with a single call in the constructor:
That's all it takes! You can now skip all calls to authorize in the individual methods. Laravel will now automagically check the right abilities for any given action. It's pretty magical actually.
Laravel's built-in gate works by hard-coding all authorization logic in your app. If you want to manage your users' roles and permissions via the database, check out Bouncer, which integrates effortlessly with Laravel's built-in authorization system.
Questions? Comments? Complaints?
Ping me on Twitter.
Let's take a look at a neat little feature coming in PHP 7.1: easily converting any callable into a proper Closure using the new Closure::fromCallable() method.