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

Nova Permissions Laravel Package

pktharindu/nova-permissions

A Laravel Nova package for managing roles and permissions with spatie/laravel-permission. It adds Nova resources to create, edit, and assign roles and permissions, giving administrators a simple UI for user access control.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is for Nova installations that outgrew boolean is_admin flags but don't want a full RBAC database sync. Its central design decision — roles are rows in the database, permissions are strings in your codebase — is the right one for Nova specifically: permission strings must mirror resource/ability pairs that change with every deploy, while role assignment is runtime data your ops team edits.

Prerequisites: a working Nova installation, PHP 8.x, and a User model on the default guard. Nova 5 is supported as of v3.1.0 — if you're on Nova 4, v3.0.x remains the compatible line, so pin accordingly. Because Nova 5 support landed in the same release as a wall of Dependabot bumps, be aware the maintainer explicitly retargeted compatibility: verify the Authorizable/resource authorization path on your Nova version before assuming parity with older docs.

Minimal path:

  1. Install and publish.

    composer require pktharindu/nova-permissions
    php artisan vendor:publish --provider="Pktharindu\NovaPermissions\NovaPermissionsServiceProvider"
    php artisan migrate
    

    Read the published migration before running it. It tells you the exact table and pivot names, which you'll need for seeders and for debugging with DB::table().

  2. Register the super-admin bypass. In NovaServiceProvider::boot() (or AuthServiceProvider), register a callback so a small set of users skips all role checks. Without this you will lock yourself out of Nova on the first deploy with an empty roles table.

  3. Define the permission catalogue in code. This is the file you'll edit on every feature branch — it's the single place where "viewAny post", "delete post", etc. are declared. Locate the base class the package expects you to extend; the README shows a small array-returning method.

  4. Attach the trait to App\Models\User. It provides role assignment and permission checks (hasRole, hasPermissionTo, can).

  5. First use case. Create a Support role in the Nova Role resource, tick only viewAny on one resource, assign it to a non-admin test user, and confirm the sidebar hides everything else. That five-minute loop — create role, tick boxes, log in as that user — is the entire day-to-day workflow of the package, and it's worth doing before you write any policies.

Where to look first: config/nova-permissions.php (table names, user model, role resource class), the published Role Nova resource (what the admin UI actually offers), and the base permission class under vendor/pktharindu/nova-permissions/src. The package is small — read the whole src/ directory in twenty minutes; you'll understand the authorization chain better than the README explains it.

Implementation Patterns

Colocate abilities with the resource they protect. Rather than one giant permission array in a service provider, give each Nova resource a static method returning its own ability→string map. When you add a delete ability to Post, the permission string is in the same diff as the ability itself, and code review catches the omission.

class Post extends Resource
{
    public static function permissions(): array
    {
        return [
            'viewAny' => 'viewAny post',
            'view'    => 'view post',
            'create'  => 'create post',
            'update'  => 'update post',
            'delete'  => 'delete post',
        ];
    }
}

Derive the catalogue instead of hand-listing it. Once the pattern above is in place, aggregate it in one loop over Nova::availableResources(request()) and merge the results. New resources then appear in the Role UI automatically, and you never debug a resource that silently isn't gated because someone forgot to register it.

Treat permission strings as a public API. They're written into the role_permission pivot at the moment an admin ticks a box. Renaming view post to posts.view in a later deploy orphans every existing grant and silently revokes access. Adopt one convention ({ability} {resource}, lowercased) on day one and never deviate. If you must rename, write a migration that rewrites the pivot.

Seed roles, don't hand-create them. Roles are data, so a fresh environment has none. Ship an idempotent seeder keyed on role name that creates the baseline roles (Super Admin, Support, Content Editor) and attaches a sensible permission set. Run it in every deploy after migrations. The Nova UI is for adjustments, not initial state — otherwise staging and production drift within a week.

Programmatic checks in your own code. The package registers Laravel gates, so $user->can('view post') works in controllers, policies, Blade, and Nova resolvers alike. Prefer wrapping strings in a small enum or constants class so a typo fails at compile-ish time rather than returning false mysteriously.

Guard the Role resource itself. The Role resource lets anyone who can see it grant themselves anything. Add a canSee/canRun guard tied to a manage roles permission, and add a check preventing a user from removing their own super-admin role.

Testing pattern. RefreshDatabase + the role seeder + actingAs($supportUser)->get('/nova/resources/posts') and assert the response/nav. Test the negative case — the resource hidden — because the positive case is usually true for structural reasons even when your permission wiring is broken. Since v3.1.0 spans both Nova 4 and Nova 5, run this suite against each Nova major you support before bumping.

Gotchas and Tips

  • Two authorization layers, one of which wins silently. Nova resources check authorizeToViewAny/authorizeToView; a Laravel policy on the model is also consulted. If a policy returns true, the package's ability may never be evaluated. When something is unexpectedly visible, dump both: Gate::forUser($user)->check('view post') and the resource's static authorizedToViewAny($request).

  • Gate::before returning false blocks everything. The super-admin bypass is usually implemented via Gate::before. Returning false (rather than null) from it denies every ability for every user, including the ones you intended to allow. Return true or null only.

  • Stale permissions accumulate. Delete a resource's ability from the catalogue and its row stays in the pivot forever, invisible in the UI. Add a reconciliation command that prunes pivot rows not present in the current catalogue, and run it in the same deploy step as the seeder.

  • Opt-in abilities are the ones you forget. restore, forceDelete, attachAny, detachAny, runAction, runDestructiveAction, and lens visibility all need explicit entries. An unticked attachAny on a BelongsToMany field produces a confusing UX where the field appears but the attach button 404s. Enumerate every ability Nova exposes for each resource, once, and put the list in your team's PR checklist.

  • Ability name collisions. Two resources both lowercasing to user (e.g. User and Nova\User from a package) produce duplicate strings. Prefix ambiguous ones.

  • Config caching. After editing the permission catalogue, php artisan optimize:clear. In production, a cached config plus an opcache means the new catalogue is invisible until you explicitly clear — the symptom is "the box doesn't appear in the Role UI."

  • Don't run two RBAC systems. If the host app already uses another permission package, you now have two sources of truth and two pivot tables. Either consolidate on this one for Nova-only gating, or wrap this package's checks through the existing system. Pick before you write code, not after.

  • Vendor risk is real but manageable. 136 stars and zero dependents means the package is effectively a maintained snippet, not an ecosystem. It's MIT-licensed and small enough to read in full — if Nova's next major breaks it, fork it into app/ or a local path repository rather than waiting. Pin the version in composer.json ("~3.1" on Nova 5, "~3.0" on Nova 4) and re-run your Nova upgrade test suite against it specifically. Note that v3.1.0 is functionally a one-line PHP change (Nova 5 compatibility) plus a dozen npm dev-dependency bumps — the runtime surface you read in src/ is unchanged, so an existing mental model still holds.

  • Debugging checklist, in order: php artisan optimize:clear → Gate::abilities() to confirm the ability is registered at all → Gate::forUser($u)->check('string') with the exact string from the pivot table → check the user's roles via $u->roles->pluck('name') → check the resource's authorizeToViewAny. That sequence isolates the layer in under two minutes.

  • Extension points: the published Role Nova resource, the config file, and the auth callback are the three sanctioned override surfaces. Anything beyond that means editing vendor/ — so make the fork decision early, and vendor it under your own namespace with a comment pointing to the upstream commit.

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.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core