Skip to content
CAI
Software that uses CAICheck a score

spatie/laravel-permission

68.9

Adequate · 25 September 2026

3.2k

lines of production code

PHP

primary language

4

measurements over time

CAI band scale
CAI trend line
CAI lens gauges

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

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.