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 Shield Laravel Package

rupadana/filament-shield

Filament Shield adds role and permission management to Filament panels, wrapping spatie/laravel-permission with a ready-made UI, policy generation, and per-resource permission checks so you can control who sees and does what.

View on GitHub
Deep Wiki
Context7

Getting Started

Filament Shield wires spatie/laravel-permission into Filament panels so that every Resource, Page and Widget gets a matching permission (e.g. view_any_user, create_user, page_Settings) and a generated Policy. You almost never write permission strings by hand — you create Filament components and let Shield derive them.

Minimum sequence on a fresh app:

composer require rupadana/filament-shield
php artisan shield:install          # publishes config + migration, wires the plugin, offers --fresh/--tenant
php artisan shield:super-admin --user=1

Then register the plugin on your panel (->plugin(FilamentShieldPlugin::make())) and run the generator:

php artisan shield:generate --all --panel=admin

--all covers resources, pages, widgets and custom permissions in one pass; without a selector the command is a no-op and you will wonder why nothing appeared.

Where to look first

  1. config/filament-shield.php — the source of truth for permission naming, discovery and the generated Role resource.
  2. app/Policies/ — the generated policies; these are yours to edit.
  3. App\Models\User — must implement FilamentUser and use HasRoles; Shield's super-admin bypass is a Gate::before hook, not a trait.

First real use case: a resource that only some users may touch. Create the Filament resource, run shield:generate --all, seed/assign the resulting permissions to a role in the Role resource UI (or via seeder), then confirm $user->can('update_post') flips the table's edit button on and off. That round-trip — resource → generate → assign → verify — is the loop you will run dozens of times per project.

3.0.5 is a patch on the 3.x line with no published changelog detail beyond the diff; there are no migration steps, config key renames or command signature changes to action. Treat it as a drop-in upgrade and keep using the 3.x workflow above.

Implementation Patterns

Generate on every structural change. The reliable habit is: add/rename/remove a Resource, Page or Widget → php artisan shield:generate --all → php artisan permission:cache-reset. Committing the generated policies keeps diffs reviewable; regenerating blindly in CI will silently drop hand edits unless policies.merge is enabled.

Scope the generator per component. On large apps --all is slow and noisy. Target one thing at a time:

php artisan shield:generate --resource=PostResource --panel=admin
php artisan shield:generate --page=Settings --panel=admin
php artisan shield:generate --option=policies_and_permissions

Keep policies thin, permission-aware. Generated policies are plain Laravel policies; put real rules in them and let Shield's permission names be the first line of defence:

public function update(User $user, Post $post): bool
{
    return $user->can('update_post') && $post->author_id === $user->id;
}

Super admin without sprinkling checks. Configure super_admin.define_via_gate => true and let the Gate::before hook short-circuit. Do not add if ($user->hasRole('super_admin')) to policies — you will duplicate the bypass and make tests non-deterministic.

Seed roles, not clicks. For repeatable deploys, use Shield's seeder to snapshot the permission matrix into a ShieldSeeder/RoleSeeder and run it before any UserSeeder. Ordering matters: assigning a role in a user seeder before roles exist will fail silently in some drivers.

Multi-panel. Each panel has its own context; pass --panel= and publish the Role resource per panel (shield:publish --panel=admin) if admins and tenants need separate navigation. Permissions accumulate across panels, so keep naming consistent via permissions.separator and permissions.case and set them before the first generation.

Tenancy. With tenant_model configured, install with --tenant so the Role resource is tenant-scoped and policies receive the team context. Verify with a second tenant early — cross-tenant leakage here is the classic failure mode.

Custom permissions and non-Filament surfaces. Add bespoke permissions in the Role resource UI (or the custom_permissions config) for things with no Filament component — API endpoints, queued jobs, artisan actions — and check them with the same $user->can(...) calls. For APIs, point auth_provider.guard at your Sanctum guard so the same matrix applies.

Blade and non-panel code just use the standard directives: @can('view_any_post'). No Shield-specific syntax is needed once permissions exist.

Gotchas and Tips

  • Generation is not automatic. New resources have no permissions until you run the generator; the UI then hides the component for everyone but super admins, which reads like a bug rather than a missing command.
  • Spatie's permission cache. After any permission or role change outside the UI (seeders, deploy scripts), run permission:cache-reset or optimize:clear. Stale cache produces "works locally, 403 in staging" reports.
  • permissions.case and separator are permanent-ish. Changing camel/upper/lower or the separator after permissions exist in the DB creates a parallel set of orphaned permissions. Decide on day one; if you must change it, regenerate and prune old rows.
  • Pages and widgets need opt-in visibility. Shield generates page_* permissions, but the component still needs public static function canAccess() / canView() wired to them, otherwise the permission is decorative.
  • Super admin in tests. Gate::before bypasses everything, so actingAs($superAdmin) can mask missing permissions. Write at least one test as a plain role-holding user, and consider disabling super_admin.enabled in the test config.
  • Renamed resources leave skeletons. Deleting a resource does not delete its permissions or policy. Prune both, or your Role resource slowly fills with dead entries.
  • --all vs. explicit options. Mixing flags can double-generate and overwrite customized policies. Prefer one flag per invocation and keep generated policies under version control so accidental overwrites are visible.
  • Debugging a 403: check, in order — does the permission row exist (permission.table), is it on the role, is the cache cleared, does the policy method actually return true, is super_admin.intercept_gate interfering. That order resolves nearly every report.
  • Extension points: shield_resource (swap or subclass the Role resource), policies.path + merge, and the per-type except/generate lists in config. Reach for those before editing files inside vendor/.
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.
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel