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

ferdiunal/nova-shield

Nova Shield is a Laravel Nova package for managing roles and permissions. It lets you control access to Nova resources, actions, and tools through a simple interface, making authorization easier to configure and maintain.

View on GitHub
Deep Wiki
Context7

Getting Started

Before you install anything, check the version matrix. This package sits between two private/versioned dependencies (Laravel Nova and, almost certainly, spatie/laravel-permission), and with ~11 stars and zero dependents you are effectively the QA team. Read composer.json on the repo first and confirm the nova and spatie/laravel-permission constraints match what you actually run — Nova 4 vs Nova 5 is the usual break point.

composer require ferdiunal/nova-shield

# Publish whatever the provider exposes (config, migrations, lang)
php artisan vendor:publish --provider="Ferdiunal\NovaShield\NovaShieldServiceProvider"

php artisan migrate

Then look at src/NovaShieldServiceProvider.php (or src/Providers/) and src/ root before touching the UI. The three things you need to confirm from source, because they aren't documented reliably:

  1. Whether the Role / Permission Nova resources auto-register or must be added to NovaServiceProvider::resources().
  2. Whether it ships its own pivot tables or reuses Spatie's roles / permissions / model_has_roles migrations.
  3. Which guard name it defaults to (web, nova, or config-driven).

First use case: don't try to model your whole authorization scheme on day one. Install it, create a single super-admin role, assign it to one user, and verify that a permission check gated through Nova actually flips. That end-to-end loop (Nova UI → DB → gate → UI) is the thing you're validating, not the CRUD screens.

A quick smoke test:

// tinker
$user = App\Models\User::first();
$user->assignRole('super-admin');
$user->can('viewAny', App\Models\Post::class); // with a Gate::before or policy using permissions

If the package ships a canSee/authorizable integration, this is also where you confirm Nova's own authorization hook is wiring permissions in rather than just showing you a table.

Implementation Patterns

Treat it as an admin UI over Spatie, not as an authorization framework. The durable value is the Nova screens for editing roles and attaching permissions; everything downstream (policies, gates, middleware) should still be plain Laravel + Spatie API calls. That way the package is replaceable.

Seed roles and permissions in code, edit assignments in Nova. Permissions should be generated from a canonical list, not clicked in by hand — otherwise production and staging drift within a week.

// database/seeders/PermissionSeeder.php
foreach (['view', 'create', 'update', 'delete'] as $action) {
    foreach (['Post', 'User', 'Invoice'] as $model) {
        Permission::findOrCreate("{$action} {$model}", 'web');
    }
}

$admin = Role::findOrCreate('admin', 'web');
$admin->syncPermissions(Permission::where('guard_name', 'web')->get());

Run this in every deploy before anything that reads permissions, and make it idempotent (findOrCreate + syncPermissions, never create).

Bootstrap super-admins outside the permission tables. A common pattern that survives the package being swapped out:

// AuthServiceProvider::boot()
Gate::before(function ($user, $ability) {
    return $user->hasRole('super-admin') ? true : null; // null, not false
});

Returning null (not false) is essential — it lets normal policies still run for everyone else.

Gate Nova resource access on permissions, not roles. Roles rot; permissions are what you actually reason about.

public static function authorizedToViewAny(Request $request): bool
{
    return $request->user()?->can('view User') ?? false;
}

Use a single guard consistently. If Nova authenticates via the web guard (the default), set config/auth.php guards and the Spatie guard_name to match. If you have a separate nova guard, every role and permission row needs guard_name = 'nova', and your policies must check that guard explicitly. Mixed guards is the number-one source of "the permission is there but can() returns false."

For multi-tenant or team-scoped setups: Spatie supports teams via config/permission.php (teams => true), but this package's UI almost certainly writes to the global tables without a team context. If you need teams, verify against source before committing — and be prepared to extend the resource or add a team_id filter yourself via Nova's indexQuery.

Cache aggressively, invalidate deliberately. Spatie caches the permission map. Any role/permission write performed outside of Eloquent events (raw SQL imports, DB::table updates) will leave stale cache until you run:

php artisan permission:cache-reset   # or Cache::forget(config('permission.cache.key'))

The Nova UI goes through Eloquent, so it's fine — your seeders and imports are the risk.

Gotchas and Tips

  • Audit before production. 11 stars, 0 dependents, single maintainer, last release Oct 2024. Read src/ end to end — it's a small package, so this takes an hour. Look specifically for withoutMiddleware, authorize() calls on the resources, and whether the permission resource is itself policy-protected (a permission-editing screen that any Nova user can reach is a privilege-escalation hole).
  • Nova is not on Packagist. If composer require fails with an unresolvable laravel/nova constraint, you likely need repositories entries or a Nova version the package predates. Check open issues for "Nova 5" support.
  • Migration collisions. If the package publishes Spatie's migrations, and you already have them, you'll get duplicate-table errors. Publish selectively (--tag=config) and point the package at your existing tables via config/permission.php rather than running its migrations.
  • guard_name mismatches surface as silent denials. $user->can('x') returns false with no error when the permission exists under a different guard. Debug with Permission::all()->pluck('guard_name','name') and compare to config('auth.defaults.guard').
  • Cache bites during testing. Permissions cached between tests in the same process cause flaky suites. Clear in setUp() or set config(['permission.cache.store' => 'array']) in phpunit.xml.
  • N+1 on the role index. Nova's relationship fields for permissions will lazy-load per row. Use indexQuery with ->with('permissions') or accept the slowdown on small role counts (which is the normal case).
  • Extension points are your escape hatch. Customize via the Nova resource's fields(), add your own actions(), and override authorizedTo* per resource rather than forking. If the package's resources are final or the fields are hardcoded, that's a strong signal to write a 100-line custom Nova resource over Spatie instead — this package is small enough that reinventing it is cheap and removes a maintenance risk.
  • Pin the version. With a single maintainer and low adoption, use an exact constraint ("ferdiunal/nova-shield": "1.2.3") and review diffs on upgrade, or vendor the source into app/Nova if the license permits (MIT — it does).
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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons