spatie/laravel-permission
68.9
Adequate · 25 September 2026
3.2k
lines of production code
PHP
primary language
4
measurements over time
What this system is
This system is a Laravel package that provides role-based and permission-based access control for applications, supporting multi-tenancy through team-based scoping. It enables developers to define roles and permissions, enforce access via middleware and Blade directives, and manage these entities through console commands and Eloquent models. The implementation includes caching mechanisms, wildcard permission support, and comprehensive testing to ensure robust authorization logic.
How it got here
2015 — Laravel 12 modernization and team support
9 changes.
The project modernized its codebase to support Laravel 12 and PHP 8.3, replacing legacy components with full-featured Eloquent models, new Artisan commands, and dedicated exception classes. This period also introduced team-based permission scoping, bulk assignment capabilities, and formal contract interfaces to improve extensibility and developer experience.
2017–2026 — Multi-tenancy and middleware expansion
9 changes.
This period focused on introducing multi-tenancy support through team-based scoping in database migrations and configuration, alongside the addition of new middleware classes with BackedEnum support. The work was heavily complemented by the creation of a comprehensive test suite covering models, traits, commands, and integration scenarios to ensure the reliability of these new features.
Features
Add role, permission, and combined role-or-permission middleware with BackedEnum support
The src/Middleware directory now includes three new middleware classes—PermissionMiddleware, RoleMiddleware, and RoleOrPermissionMiddleware—that enforce access control by checking user roles and permissions. These middlewares support Laravel's BackedEnum types for roles and permissions, allowing developers to use enum values directly in route definitions or middleware declarations. Each middleware validates the authenticated user (including Passport client credentials) and throws an UnauthorizedException if the required role or permission is not present, providing a consistent and type-safe way to protect routes.
src/Middleware · high confidence
IDE support for Blade directives and modernized CI configuration
This release adds an \ide.json\ file to provide IDE autocompletion for the package's Blade directives (such as \@role\, \@hasrole\, \@unlessrole\, and their end tags). It also replaces the legacy Travis CI and Scrutinizer configurations with modern tooling, introducing \phpstan.neon.dist\ for static analysis, \pint.json\ for code formatting, and updated GitHub Actions badges in the README. The \.gitattributes\ file is updated to exclude these new configuration files and other development artifacts from package exports.
(repo-wide) · high confidence
Initial release of the permission configuration file
This change introduces the \config/permission.php\ file, establishing the default configuration for the Spatie Laravel Permission package. It defines the Eloquent models for permissions, roles, and teams, sets the default database table names and pivot column keys (including \model\_id\ and \team\_id\), and configures feature toggles for teams, wildcard permissions, Octane resets, and exception display. It also sets the default team resolver class and enables the registration of permission check methods and events by default.
config · high confidence
Introduction of new contract interfaces for permissions, roles, and wildcards
The library now exposes formal PHP interfaces for its core domain objects, allowing developers to type-hint against abstractions rather than concrete implementations. New contracts include \Permission\ and \Role\, which define methods for finding entities by name or ID (supporting both strings and BackedEnums), creating entities if they do not exist, and checking permissions. A \PermissionsTeamResolver\ interface is introduced to support configurable team-based permission scoping, while a \Wildcard\ interface defines the contract for custom wildcard permission verification logic.
src/Contracts · high confidence
New Artisan commands and events for managing roles, permissions, and teams
This release introduces a suite of new Artisan commands to manage permissions and roles directly from the console: \permission:assign-role\ to assign a role to a user, \permission:create-permission\ and \permission:create-role\ to create new entries, \permission:show\ to display a table of roles and permissions per guard, \permission:cache-reset\ to flush the permission cache, and \permission:setup-teams\ to generate the migration required for the teams feature. Additionally, the package now dispatches dedicated events (\PermissionAttachedEvent\, \PermissionDetachedEvent\, \RoleAttachedEvent\, \RoleDetachedEvent\) whenever roles or permissions are attached or detached from a model, allowing applications to react to these changes. The \PermissionLoader\ class has been removed in favor of the new \PermissionRegistrar\ for handling permission registration and caching.
src · high confidence
New HasAssignedModels trait for bulk role assignment to multiple models
The \src/Traits\ location introduces a new \HasAssignedModels\ trait that allows roles to be assigned to, removed from, or synced with multiple models in a single operation. This trait provides \assignToModels\, \removeFromModels\, and \syncModels\ methods, which handle grouping models by their morph class and attaching/detaching them via the underlying \model\_has\_roles\ pivot table. It also includes a new \RefreshesPermissionCache\ trait to automatically clear the permission cache when models are saved or deleted, ensuring consistency across bulk operations.
src/Traits · high confidence
Behavioural changes
New exception classes for guards, teams, and wildcard permissions
The library now includes dedicated exception classes to provide clearer error reporting for specific scenarios. New exceptions handle guard mismatches (\GuardDoesNotMatch\), role and permission existence checks with guard context (\RoleAlreadyExists\, \RoleDoesNotExist\, \PermissionAlreadyExists\, \PermissionDoesNotExist\), and teams configuration issues (\TeamModelNotConfigured\, \TeamsNotEnabled\). Additionally, \UnauthorizedException\ has been enhanced to optionally display the specific roles or permissions required for access, and new exceptions (\WildcardPermissionInvalidArgument\, \WildcardPermissionNotImplementsContract\, \WildcardPermissionNotProperlyFormatted\) support the wildcard permission feature by validating input types and formats.
src/Exceptions · high confidence
Removed legacy migration stub using deprecated Schema::dropTable
The \create\_permission\_tables.php.stub\ migration file has been removed. This stub previously utilized the deprecated \Schema::dropTable\ method in its \down()\ method to reverse migrations for the roles, permissions, and their pivot tables. Its removal indicates a shift away from this legacy migration template, likely to prevent users from generating migrations that rely on outdated or removed Laravel schema builder methods.
resources/migrations · high confidence
Replaced minimal model stubs with full-featured Permission and Role models
The \src/Models\ directory now contains complete Eloquent implementations for \Permission\ and \Role\ (replacing the previous minimal stubs in \src/models\). These models now support guard names, BackedEnum values for names/IDs, and team-based scoping. They provide static factory methods (\findByName\, \findById\, \findOrCreate\) that utilize the permission cache for performance, and define proper many-to-many relationships with users and each other via the \PermissionRegistrar\ configuration.
src/Models · high confidence
Updated database migration stubs to support team-based scoping
The database migration stubs have been updated to support multi-tenancy via teams. The \create\_permission\_tables.php.stub\ now conditionally adds a \team\_foreign\_key\ column to the roles, model\_has\_permissions, and model\_has\_roles tables when the \teams\ config option is enabled, adjusting unique constraints and primary keys accordingly. A new \add\_teams\_fields.php.stub\ migration is provided to add this column and update existing tables for applications upgrading from a non-team setup. Both stubs now use \throw\_if\ for configuration validation and clear the permission cache upon execution.
database · high confidence
Test coverage
Added Pest-based test suite for Role and Permission models; Added comprehensive test suite for role, permission, and team traits; Added integration tests for Blade directives, caching, Octane listeners, and routing; Added test coverage for middleware authorization logic; Added test support infrastructure for role and permission testing; Added tests for permission and team management commands; Migrate test suite from PHPUnit to Pest.
Dependencies
Modernizes package for Laravel 12+ and PHP 8.3+ with updated tooling
This update raises the minimum requirements to PHP 8.3 and Laravel 12 (supporting up to Laravel 13), shifting the package away from older Laravel versions. It replaces the PHPUnit test suite with Pest and uses Laravel Pint for code formatting, while introducing Larastan for static analysis. The dependency structure is also modernized by requiring specific illuminate components (auth, container, contracts, database) and adding spatie/laravel-package-tools for better integration.
(dependencies) · high confidence
Written by watchdog.canine.dev from the codebase's own history, inside the signed delivery this page is composed from.
How this codebase got here
This is the PUBLIC form of this artifact. Findings are listed in full, but the details of SECURITY findings — which rule fired, in which file, on which line, and how to fix it — are deliberately withheld, and any secret-scanner results are excluded entirely. Where detail is absent here it was REMOVED FOR PUBLICATION; it is not missing from the analysis. The complete artifact is available from the repository owner.
Score
- CAI 44 → 69 (+24.9)
- Rubric changed (rubric-2026.08.15 → rubric-2026.09.15) — scores are not directly comparable.
Lenses
- Code Health 99 → 97 (-2.6)
- Architecture 94 → 95 (+1.0)
- Maturity 61 → 59 (-1.8)
- Readiness 30 → 84 (+53.3)
- Security 36 → 67 (+31.0)
Resolved (20)
- Coverage not measured — test suite did not build
- Dimension evaluation failed
- Duplicated block (7 lines × 2) (src/PermissionServiceProvider.php)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- No exposed public API
- No tests found
- Test reliability not included
- The 'What It Does' example uses Laravel's default can function but the README does not explain how to check for a permission via can, leaving this usage unqualified by whether it is the package's own gate or another service. (README.md)
New (32)
- Dependency hygiene PARTLY measured — Composer dependencies read, no committed lock to grade for currency
- Documentation: no installation or build instructions (docs/basic-usage/new-app.md)
- Duplicated block (10 lines × 2) (src/Traits/HasPermissions.php)
- Duplicated block (12 lines × 3) (src/Middleware/PermissionMiddleware.php)
- Duplicated block (14 lines × 2) (src/Middleware/PermissionMiddleware.php)
- Duplicated block (19 lines × 2) (src/Traits/HasPermissions.php)
- Duplicated block (6 lines × 2) (src/Models/Permission.php)
- Duplicated block (7 lines × 2) (src/Models/Permission.php)
- Duplicated block (7 lines × 3) (src/Middleware/PermissionMiddleware.php)
- Duplicated block (8 lines × 2) (src/Models/Permission.php)
- HasRoles.hasRole (cognitive 20) (src/Traits/HasRoles.php)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- High: security finding (details withheld)
- …and 12 more
Changes since last survey
- 6 commits — 4 feature/other, 2 fixes
By area
- (repo) — 2 commits
- src/PermissionRegistrar.php — 2 commits
- (root) — 1 commit
- tests/Integration — 1 commit
Notable commits
- fix: Merge pull request #2973 from AdilAzhari/fix/registrar-stale-cache-manager
- fix: fix: resolve cache manager fresh from the container in getCacheStoreFromConfig
- change: Add pint.json to export-ignore in .gitattributes
- change: Merge pull request #2972 from jackbayliss/patch-1
- change: refactor: refresh cacheManager in initializeCache instead of a local var
- change: test: remove no-op forgetInstance(Repository::class) call
Written by watchdog.canine.dev from the codebase's own history, inside the signed delivery this page is composed from.
Survey your own repository
spatie/laravel-permission was measured the same way every project in this corpus was: the same rubric, at a pinned commit, with the result published in full. Point a surveyor at a repository you know and see whether you agree with it.
About this page
- The score is its most recent published measurement, taken on 25 September 2026 at a pinned commit. It is not a live figure and does not change until the project is measured again.
- Measured at commit 6615eefac655efcd652fe394a20cbdf4f72c609f — the exact code this score is about.
- Scored under rubric-2026.09.15 — the same rubric and the same method as every other entry in this index.
- Measured by watchdog.canine.dev using codehealth-analyzer preprod-a9cd699f3cd5.