Laravel Policy - first try and problem solve
Basics
It is not uncommon seeing code like this when related to user authorisation. All the code was mixed in a single controller. Not only this adds complexity to the controller but also hard to maintain. The only advantage of this is to finish work earlier, but regret in the future.
public function update(Request $request, Post $post)
{
if ($request->user->id !== $post->user_id) {
abort(403)
}
}
Therefore, Laravel introduces gate and policy. While gate is rather a simple implementation to address the authorisation problem, policy makes authorisation more complete.
Gate
The code above can be written using gate. In the boot()
method of AuthServiceProvider
, the gate was defined in this way.
Gate::define('update-post', function ($user, $post) {
return $user->id === $post->user_id;
});
It can be used in controller like this
if (Gate::allows('update-post', $post)) {
// The current user can update the post...
}
However, this can be complex since all the gate was defined by closure. It is necessary to use a more sophisticated way. Then policy was introduced.
Policy
php artisan make:policy PostPolicy --model=Post
After generated the policy, we can define the access rules in App\Policies\PostPolicy
.
public function update(?User $user, Post $post)
{
return optional($user)->id === $post->user_id;
}
The policy can be used in controller by controller method or via user model.
public function update(Request $request, Post $post)
{
// controller method
$this->authorize('update-post', $post)
// via user model
if (!$user->can('update', $post)) {
abort(403)
}
}
More usage can be seen in the Laravel documents.
Policy auto-discovery not working
The policy auto-discovery could not function well if the namespace structure was changed for models. The problem can be solved by adding a custom resolver in the boot()
method of AuthServiceProvider
.
Gate::guessPolicyNamesUsing(function ($modelClass) {
return 'App\\Policies\\' . class_basename($modelClass) . 'Policy';
});
Using guard other then default
authorizeForUser()
can be used in this scenario by specifying the guard to get the user.
$this->authorizeForUser($request->user('api'), 'update', $post);
References
[1]: Authorization - Laravel - The PHP Framework For Web Artisans
[3]: Laravel only allow owner user to access route - Stack Overflow