Appearance
Permissions
Introduction
Access control in kitloom has two layers:
kitloom/access— a tiny contract package. Modules use it to declare the permissions that open their admin pages. It has no storage and no opinion about roles.kitloom/permissions(+-filament/-nova) — the roles engine: roles, the Users → Roles screen, the Role field on the user form, and the actual checks.
Without the roles engine every declared section is open to any user who can enter the admin, so a site works out of the box. Installing kitloom/permissions switches access control on.
Content capabilities
Posts, pages, taxonomies, comments, media and users use the WordPress capability names — edit_posts, edit_others_posts, publish_pages, manage_categories, list_users — including meta capabilities for a single record (edit_post, delete_post). A site checks them through Laravel's Gate as usual:
php
Gate::allows('edit_post', $post);
$user->can('publish_posts');Custom post types and taxonomies can get capabilities of their own (edit_books, manage_genre_terms) — the roles module turns this on.
Declaring a module's permissions
Every other admin page asks about itself: Gate::allows(static::class). The module declares which permission opens that page — one permission per menu item — through a PermissionProvider:
php
<?php
namespace Kitloom\Redirects\Filament;
use Kitloom\Access\Contracts\PermissionProvider;
use Kitloom\Access\Permission;
use Kitloom\Redirects\Filament\Resources\NotFoundLogResource;
use Kitloom\Redirects\Filament\Resources\RedirectionResource;
final class RedirectsPermissions implements PermissionProvider
{
public function permissions(): iterable
{
yield new Permission(
name: 'manage_redirections',
group: 'Redirections',
covers: [RedirectionResource::class],
label: 'Manage redirects',
);
yield new Permission(
name: 'manage_404_log',
group: 'Redirections',
covers: [NotFoundLogResource::class],
label: 'See and clear the 404 log',
);
}
}Tag it in the admin package's provider:
php
use Kitloom\Access\Permission;
$this->app->tag([RedirectsPermissions::class], Permission::PROVIDER_TAG);| Argument | Meaning |
|---|---|
name | Capability name, WordPress style: manage_redirections |
group | Heading on the roles screen, usually the module name |
covers | Classes of the pages, resources or tools this permission opens |
label | Human-readable text next to the checkbox |
roles | Roles that receive it the first time the site sees it. Default: ['administrator'] |
like | Roles that hold this other permission receive it too — for a permission split off an existing one |
On a Filament resource guarded by its section, use the GuardedBySection trait from kitloom/wp-schema-filament — every ability (canViewAny, canCreate, canEdit…) then asks Gate::allows(static::class).
A section nobody declared stays open; with the roles module installed it requires manage_options.
Installing the roles engine
bash
composer require kitloom/permissions kitloom/permissions-filament
# or
composer require kitloom/permissions kitloom/permissions-novaGive the user model roles:
php
use Kitloom\Permissions\Concerns\HasRoles;
class User extends Authenticatable
{
use HasRoles;
}Run the migrations — they create the role tables, the default roles (administrator, editor, author, contributor, subscriber) and make the first user an administrator if nobody is one:
bash
php artisan migrateLet the panel in whoever may read, like wp-admin:
php
public function canAccessPanel(Panel $panel): bool
{
return $this->can('read');
}php
Gate::define('viewNova', fn (User $user): bool => $user->can('read'));How roles behave
- A permission a module declares reaches the roles named in
rolesonce, the first time the site sees it. After that, roles belong to the site's managers: a permission they remove is not given back. - A permission from a deactivated module is kept, so activating the module restores access as it was.
- Roles are stored with spatie/laravel-permission, but only behind kitloom's own contracts (
RoleRepository,UserRoles,PermissionChecker). Keep it replaceable:- check access through the Gate only —
Gate::allows(),$user->can(); - do not call spatie methods (
hasRole(),hasPermissionTo()) or itsrole:/permission:middleware; - do not publish spatie's migration — the package ships its own.
- check access through the Gate only —
To assign a role in code — a seeder, a factory — use the contract:
php
use Kitloom\Permissions\Contracts\UserRoles;
app(UserRoles::class)->setRole($user, 'editor');