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

Panel Roles Laravel Package

shanerbaner82/panel-roles

Panel Roles is a Laravel package for managing roles and permissions inside Filament panels. Define roles, assign them to users, and restrict access to panel resources and pages with an easy-to-configure setup that keeps authorization consistent across your admin panels.

View on GitHub
Deep Wiki
Context7

Getting Started

shanerbaner82/panel-roles is a small Filament panel plugin that handles the two role chores every admin panel needs: giving brand-new users a default role, and keeping certain roles out of the panel entirely.

Install it and register the plugin inside your panel provider:

composer require shanerbaner82/panel-roles
use Shanerbaner82\PanelRoles\PanelRoles;

class AdminPanelProvider extends PanelProvider
{
    public function panel(Panel $panel): Panel
    {
        return $panel
            ->plugin(PanelRoles::make()
                ->roleToAssign('developer')
                ->restrictedRoles(['admin', 'developer']),
            );
    }
}

As of v0.0.3 both roleToAssign() and restrictedRoles() are mandatory. Earlier versions worked without them; a bare PanelRoles::make() will no longer behave as before, so treat this as a breaking change if you are upgrading.

First things to check, in order:

  1. The roles you name actually exist in whatever role backend you use (typically spatie/laravel-permission — run your seeder before touching the panel).
  2. restrictedRoles does not contain a role you need in order to administer the panel. Locking yourself out on a fresh install is the classic first-run failure.
  3. Log in as a fresh user and confirm the assigned role landed.

The first real use case: a /admin panel where self-registered users get developer automatically, while admin and developer roles are the only ones allowed into the panel.

Implementation Patterns

Keep the config in the panel provider, not in config files. The plugin is fluent and panel-scoped, which is its main advantage over a global config: two panels can enforce different rules.

// AdminPanelProvider — staff only
->plugin(PanelRoles::make()
    ->roleToAssign('staff')
    ->restrictedRoles(['admin', 'staff', 'super-admin']),
)

// CustomerPanelProvider — customers only
->plugin(PanelRoles::make()
    ->roleToAssign('customer')
    ->restrictedRoles(['customer']),
)

Drive role names from constants or config rather than raw strings. Role names appear in seeders, policies, tests, and this plugin. A single Roles class or config/roles.php prevents the slow drift where the panel provider says 'super-admin' and the seeder creates 'superadmin'.

Seed roles idempotently before the panel is ever hit. Standard pattern:

foreach (['admin', 'developer', 'staff'] as $role) {
    Role::findOrCreate($role, 'web');
}

Run this in a seeder wired into deployment, not just DatabaseSeeder for local dev — otherwise production's first registration silently fails to assign a role.

Pair it with canAccessPanel(). The plugin's restriction is one gate; your User::canAccessPanel() is another. Keep the logic consistent and lean on the plugin for the "is this role in the allow-list" part rather than duplicating a hardcoded array. If you need an exception (e.g. a suspended user who is also an admin), do it in canAccessPanel() and let the plugin handle the coarse role check.

Test it with a role-aware factory state. In Pest/PHPUnit:

$user = User::factory()->create();
$user->assignRole('admin');

$this->actingAs($user)
    ->get('/admin')
    ->assertOk();

$outsider = User::factory()->create();
$outsider->assignRole('customer');

$this->actingAs($outsider)
    ->get('/admin')
    ->assertForbidden();

Add one test asserting a freshly registered user receives roleToAssign — that is the behaviour most likely to regress between releases.

Integration tip: the assignment fires at registration time, so any custom registration flow (Fortify, Breeze, a Filament registration page, or an invite-based flow) should be verified to go through the path the plugin hooks. If you create users via an admin UI or an import, check whether the role is applied or handle it explicitly with assignRole().

Gotchas and Tips

  • v0.0.3 is a breaking change. Both methods are now required. If you have an existing install, audit every panel provider — a missing restrictedRoles() may either throw or, worse, leave the panel unrestricted. Pin the version in composer.json and read the release notes before bumping; pre-1.0 packages at 0.0.x make no stability promises.
  • Restricting a role does not remove it from already-logged-in sessions. A user whose role is newly added to restrictedRoles keeps their session until it expires or they log out. Force a re-check (or php artisan sessions:clear / cache clear) if you are tightening access urgently.
  • Role names are exact-match strings. 'Admin' ≠ 'admin', and a typo produces a panel that silently allows everyone or nobody. Log the resolved roles at boot in local dev if you are unsure.
  • Guard scoping. If you run multi-guard setups (e.g. separate web and admin guards), confirm the roles exist on the guard the panel uses. spatie/laravel-permission caches permissions; after changing role definitions run php artisan permission:cache-reset.
  • Don't restrict the role you are logged in as while iterating. Temporarily keep restrictedRoles broad, verify access, then tighten. Keep a console tinker route to re-add your own role if you do lock yourself out.
  • The assign step needs the role to exist at the moment of registration. If a user registers before your seeder has run (fresh deploy, new environment), the assignment fails quietly. Make seeding part of the deploy pipeline rather than a manual step.
  • Debugging: the fastest signal is whether a brand-new user has any role at all. $user->getRoleNames() in tinker tells you whether the problem is assignment (plugin config) or access (panel/policy). Don't start by editing canAccessPanel().
  • Extension points are thin at 0.0.x. There are no documented events or contracts here, so avoid subclassing PanelRoles or decorating its internals — you'll be rewriting on every patch release. If you need conditional logic (assign role X only for invited users), keep it in your registration flow and leave the plugin to the static default.
  • Config quirk to watch: the fluent calls are order-independent, but calling make() with no arguments and relying on defaults is exactly what v0.0.3 removed. Treat the explicit calls as documentation of intent and don't factor them out into a helper that "just returns a configured instance" — being able to read the roles straight from the panel provider is worth the repetition.
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.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core