Laravel policies are a great way to protect your resources without cluttering your controllers with all sorts of conditionals. I set up a policy to ensure the following:
- Guests are only allowed to view published posts
- Authenticated users are able to view all posts
- Authenticated users are also allowed to create, edit, and delete posts
I wrote a policy class for these rules (abbreviated example):
class PostPolicy {
use HandlesAuthorization;
/**
* Determine whether the user can view the post.
*
* @param \App\User $user
* @param \App\Post $post
* @return mixed
*/
public function view(?User $user, Post $post) {
// Either the post must be published or
// the user must be authenticated.
return $post->isPublished || filled($user);
}
/**
* Determine whether the user can create posts.
*
* @param \App\User $user
* @return mixed
*/
public function create(User $user) {
return $user->exists;
}
/**
* Determine whether the user can update the post.
*
* @param \\App\\User $user
* @param \\App\\Post $post
* @return mixed
*/
public function update(User $user, Post $post) {
return $user->exists;
}
}
In order to protect the resourceful controller, Laravel provides a helper function (authorizeResource) that can be placed in the constructor. However, although I was authenticated, I kept hitting a 403 unauthorized error when attempting to show or edit a post.
It turned out, the solution was simple: don't forget to use model binding in your resourceful controller. So instead of using int $id in the show / edit methods, use Post $post like this:
class PostsController extends Controller {
public function __construct() {
$this->authorizeResource(Post::class);
}
// Use Post $post instead of int $id, otherwise policies won't work!
public function show(Post $post) {
return view('posts.show', compact('post'));
}
// Use Post $post instead of int $id, otherwise policies won't work!
public function edit(Post $post) {
return view('posts.edit', compact('post'));
}
}
After this, everything worked like a charm!