Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filament Access Management Laravel Package

solution-forest/filament-access-management

Filament Access Management provides a complete roles and permissions UI for Filament admin panels. Manage roles, assign permissions and users, and control access to resources and pages with an easy-to-use interface built on Spatie's permission package.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and wire it up (about 10 minutes):

composer require solution-forest/filament-access-management
php artisan vendor:publish --tag="filament-access-management-config"
php artisan vendor:publish --tag="filament-access-management-migrations"
php artisan migrate
  1. User model — it sits on top of spatie/laravel-permission, so your User needs Spatie\Permission\Traits\HasRoles and, for the panel, Filament\Models\Contracts\FilamentUser with a canAccessPanel() method.
  2. Panel provider — register the plugin in your AdminPanelProvider:
    ->plugin(\SolutionForest\FilamentAccessManagement\FilamentAccessManagementPlugin::make())
    
  3. Config first — open config/filament-access-management.php. The keys you care about are the user_model, role_model and permission_model bindings, plus whatever permission-name/panel settings it exposes. This file is the source of truth for every "why is this denied?" question later.
  4. First use case — log in as an existing user, go to the Roles resource, create a role, and tick permissions grouped by Filament Resource/Page. Then assign the role to a user from the Users/role relation field. Log out, log back in as that user, and confirm the navigation and actions change.

Where to look when something is off: the generated roles / permissions / model_has_roles tables, then the plugin's RoleResource, then your User model's traits.

Implementation Patterns

Let the package own the permission catalogue, not your seeders. The permission names are derived from Filament Resource/Page class names, so the intended workflow is: build resources → open the Roles screen (or run the package's sync) → tick boxes. Hard-coding Permission::create(['name' => 'view_any_user']) in a seeder works until a resource is renamed and you get a ghost permission plus a 403.

Seed roles, not permissions. In RoleSeeder, look the role up by name with the correct guard and use syncPermissions() with names pulled from the DB or from the package's generated set — never duplicate the naming logic.

$role = Role::findOrCreate('editor', 'web');
$role->syncPermissions(Permission::where('name', 'like', '%PostResource%')->get());

Super-admin bypass with Gate::before. Do not build a "has every permission" role — it goes stale the moment you add a resource.

Gate::before(fn ($user) => $user->is_super_admin ? true : null);

Keep authorization in policies. The package controls which role has which permission; policy methods still decide what that permission means. Filament asks viewAny, create, update, delete, deleteAny against the resource's model — a PostPolicy with those methods is what actually makes the ticks in the UI do something. For custom Pages and Widgets, use canView() / canAccess() returning auth()->user()->can(...).

Multi-panel / multi-guard. Each panel has a guard; roles are guard-scoped in spatie. If you run an admin panel and an app panel, decide on one guard per panel and create roles under that guard, or you will silently assign a web role to a user authenticating on admin. For Filament multi-tenancy, spatie's teams feature (teams => true, team_id on roles) is the right lever, not duplicating roles.

Testing. Reset the permission cache in setUp() and use actingAs:

protected function setUp(): void {
    parent::setUp();
    app(\Spatie\Permission\PermissionRegistrar::class)->forgetCachedPermissions();
}

Then assert $this->actingAs($editor)->get(PostResource::getUrl())->assertOk() and the inverse 403. Filament's own assertCanSeeTableRecords / Livewire tests compose fine on top.

Deploy. Run the permission sync step in your deploy script after migrations and before php artisan permission:cache-reset, otherwise the first requests after deploy serve a stale cached permission map.

Gotchas and Tips

  • Guard mismatch is the #1 failure. Roles created with web while the panel uses admin produce a user whose can() is always false. Check config/auth.php guards and spatie's config/permission.php defaults.guard.
  • Stale cache. Spatie caches the permission map for 24h. After seeding, editing roles in the DB, or deploying, run php artisan permission:cache-reset (or forgetCachedPermissions() in code). "It worked, then I redeployed and everyone is locked out" is almost always this.
  • Orphaned permissions on rename. Permissions are stored as strings tied to class names. Move a resource to a subnamespace and the old permission rows linger while the new ones appear unticked. Clean up with a targeted Permission::where('name', 'like', 'App\\\\Filament\\\\Old%')->delete().
  • Model swapping. If you point role_model / permission_model at your own Eloquent models, they must extend spatie's Role/Permission and keep the same table names — and the HasRoles trait on User must resolve those classes via config/permission.php, not the package config alone. Change both.
  • Migration collisions. If spatie's migrations were already published, publishing the package's migrations can attempt to recreate existing tables. Read the published files before running migrate on an existing app.
  • FilamentUser contract is mandatory. Even with perfect roles, a User that doesn't implement FilamentUser (or canAccessPanel()) gets bounced from the panel in non-local environments — it looks like a permission bug and isn't.
  • Hidden vs forbidden. Resource-level permission denial typically hides navigation and 403s the route. If you want "visible but read-only," grant viewAny and withhold create/update rather than hiding the resource.
  • Don't edit permission rows by hand in production. The UI and any sync routine treat the generated set as canonical; manual rows drift and get pruned or duplicated.
  • Extension points: publish the views and translations (vendor:publish --tag=... — check the package's available tags) to relabel the grouped permission tree, and subclass RoleResource if you need extra fields (e.g. a description column) without forking.
  • Verify Filament major compatibility. The plugin is tightly coupled to Filament's Resource/Page APIs; check the composer.json constraint pins the Filament version you're on before upgrading either side, since a Filament major bump changes how permissions are enumerated.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib