byrcsc/laravel-comments · 1.x
Authorization.
The engine never authorizes its own methods. Register the shipped policy, override the abilities that matter, and gate the call sites.
The package registers no policy, defines no gate, and never checks whether the current user may do what you just asked it to do.
That is deliberate. approve(), react(), pin(), and the rest work in a
queued job, a seeder, and a console command, where there is no authenticated
actor to ask about. Enforcement belongs where your application calls the engine.
Register the policy
CommentPolicy is the boilerplate every installing application would otherwise
write. It is never registered for you:
use ByRcsc\LaravelComments\Models\Comment;
use ByRcsc\LaravelComments\Policies\CommentPolicy;
use Illuminate\Support\Facades\Gate;
// A service provider
Gate::policy(Comment::class, CommentPolicy::class);Then gate the call site:
// A controller
$this->authorize('approve', $comment);
$comment->approve(by: $request->user());The default abilities
| Ability | Default |
|---|---|
create | Any authenticated actor |
update | The comment's author |
delete | The comment's author |
react | Any authenticated actor |
attach | Any authenticated actor |
restore | Nobody |
forceDelete | Nobody |
approve | Nobody |
reject | Nobody |
markAsSpam | Nobody |
pin | Nobody |
unpin | Nobody |
In short: authors may edit and delete what they wrote, anyone signed in may react and attach, and nobody moderates until you say who does.
A few of those defaults are choices worth naming:
attach allows any authenticated actor, including on somebody else's
comment. A moderator adding evidence to a reported comment is as ordinary as an
author adding a screenshot to their own. Narrow it to authors by overriding.
restore denies everybody. Restoring is a moderator's undo, not an
author's — a comment removed for a reason should not come back because the
person who wrote it wants it to.
forceDelete denies everybody. It destroys a comment, its replies, its
reactions, its revisions, and its attachment rows.
Ownership
update and delete both ask the comment whether the actor wrote it:
$comment->isBy($actor);Both halves of the morph have to match, so a User and an Admin sharing a
primary key are not the same author. A guest-authored comment matches nobody,
which means ownership always denies for one: a later visitor claiming to be Jane
has nothing to prove it with.
Override one ability
Extend the class and register yours instead. Everything you do not override keeps its default:
use ByRcsc\LaravelComments\Models\Comment;
use ByRcsc\LaravelComments\Policies\CommentPolicy;
use Illuminate\Database\Eloquent\Model;
final class AppCommentPolicy extends CommentPolicy
{
public function approve(?Model $actor, Comment $comment): bool
{
return $actor?->getAttribute('is_moderator') === true;
}
public function reject(?Model $actor, Comment $comment): bool
{
return $this->approve($actor, $comment);
}
public function markAsSpam(?Model $actor, Comment $comment): bool
{
return $this->approve($actor, $comment);
}
}Gate::policy(Comment::class, AppCommentPolicy::class);The actor is nullable throughout, so an unauthenticated visitor reaches these
methods rather than being refused before them. That is what lets create be the
one place deciding whether guests may write at all:
public function create(?Model $actor): bool
{
return true; // guests may comment
}An application that does that owns its own rate limiting, which is the check that actually matters for anonymous writes and one this package deliberately does not ship.
No view ability
There is no view or viewAny here. Visibility is a query, not an ability: the
approved() scope is what decides what a visitor reads. See
moderation.
No roles, teams, or tenancy
The policy knows nothing about permission packages, roles, teams, or tenants, and it never will. Those are your application's, and the way to bring them in is the subclass above.