PHPackages                             fanmade/laravel-delegated-permissions - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Authentication &amp; Authorization](/categories/authentication)
4. /
5. fanmade/laravel-delegated-permissions

ActiveLibrary[Authentication &amp; Authorization](/categories/authentication)

fanmade/laravel-delegated-permissions
=====================================

Inheritance-based, scoped authorization for Laravel: each role delegates a subset of its own permissions to its children, and revoking cascades down the tree.

v0.1.7(1mo ago)0369MITPHPPHP ^8.4CI failing

Since Jun 23Pushed 1mo agoCompare

[ Source](https://github.com/Fanmade/laravel-delegated-permissions)[ Packagist](https://packagist.org/packages/fanmade/laravel-delegated-permissions)[ Docs](https://github.com/Fanmade/laravel-delegated-permissions)[ RSS](/packages/fanmade-laravel-delegated-permissions/feed)WikiDiscussions main Synced 2w ago

READMEChangelogDependencies (19)Versions (9)Used By (0)

Laravel Delegated Permissions
=============================

[](#laravel-delegated-permissions)

> Inheritance-based, scoped authorization for Laravel: each role delegates a subset of its own permissions to its children, and revoking cascades down the tree.

Most permission packages give you flat roles and permissions. This one is built around **constrained delegation** — a single tree per scope where a parent role can only hand its children a *subset* of what it holds, revocation flows downward, and roles are scoped to any model (per-project, per-team) or global.

The model
---------

[](#the-model)

- **One `system` role** roots every scope's tree. It implicitly holds *every*permission and is the only role without a parent.
- **A role may only hold permissions its parent holds.** A child can't be granted — or even see — a permission its parent lacks.
- **Revoking cascades down.** Removing a permission from a role removes it from every descendant that still had it.
- **Granting never cascades.** Adding a permission to a role leaves its children untouched — you delegate downward deliberately.
- **Scopes.** Roles belong to a scope (a model such as a `Project`, or `null`for the global scope). An optional **system scope sits above all others** as break-glass access — disable it once setup is done.
- **Permission groups** bundle permissions (e.g. a `tags` CRUD-set) for convenient, all-or-nothing delegation.

Requirements
------------

[](#requirements)

- PHP 8.4+
- Laravel 12 or 13

Installation
------------

[](#installation)

```
composer require fanmade/laravel-delegated-permissions
```

The migrations load automatically. Optionally publish the config and migrations:

```
php artisan vendor:publish --tag=delegated-permissions-config
php artisan vendor:publish --tag=delegated-permissions-migrations
php artisan migrate
```

Configuration
-------------

[](#configuration)

All settings live in `config/delegated-permissions.php` and read from the environment:

Env varDefaultPurpose`DELEGATED_PERMISSIONS_TABLE_PREFIX``''`Prefix for every package table, to avoid clashes.`DELEGATED_PERMISSIONS_SYSTEM_ENABLED``true`Master switch for the break-glass system role. Turn it **off** after setup.`DELEGATED_PERMISSIONS_REGISTER_GATE``true`Route `$user->can(...)` through the resolver.The system role's `scope_above_all` (whether it reaches every scope) is set in the config file.

The authorizable
----------------

[](#the-authorizable)

Add the `HasRoles` trait to the model that holds roles — usually your `User`:

```
use Fanmade\DelegatedPermissions\Concerns\HasRoles;

class User extends Authenticatable
{
    use HasRoles;
}
```

You now have `assignRole()`, `removeRole()`, `roles()`, `hasPermission()`, `permissionsIn()`, `hasRole()` and `rolesIn()`.

Building a tree
---------------

[](#building-a-tree)

Use the managers (resolve them from the container) to create permissions, a system root, and delegated child roles. Out-of-bounds grants are rejected.

```
use Fanmade\DelegatedPermissions\{PermissionManager, RoleManager};

$permissions = app(PermissionManager::class);
$roles = app(RoleManager::class);

// Catalog
foreach (['view-project', 'manage-tags', 'delete-tasks'] as $name) {
    $permissions->createPermission($name);
}

// A project's tree: system → owner → member, scoped to the project.
$system = $roles->createSystemRole();                 // global root, holds everything
$owner  = $roles->createRole('owner',  $system, ['view-project', 'manage-tags', 'delete-tasks'], $project);
$member = $roles->createRole('member', $owner,  ['view-project']); // scope inherited from the owner

// Granting beyond the parent is rejected:
$roles->createRole('intern', $member, ['manage-tags']); // throws OutOfBoundsGrant — member lacks it
```

Assign roles and check permissions, scoped to the project:

```
$user->assignRole($member);

$user->hasPermission('view-project', $project); // true
$user->hasPermission('manage-tags', $project);  // false
$user->can('view-project', $project);           // true — via the Gate integration
```

Scopes
------

[](#scopes)

Roles are isolated per scope. A role in project A grants nothing in project B:

```
$user->assignRole($ownerOfProjectA);

$user->hasPermission('manage-tags', $projectA); // true
$user->hasPermission('manage-tags', $projectB); // false
$user->permissionsIn(null);                     // global-scope permissions only
```

Roles per scope
---------------

[](#roles-per-scope)

A model may hold several roles within one scope; effective permissions are the **union** across them. The `max_roles_per_scope` config caps how many — `1` for the classic single-role model, `n` for up to n, `null`/`-1` (the default) for unlimited:

```
// config/delegated-permissions.php → 'max_roles_per_scope' => 1
$user->assignRole($designer);             // ok — first role in the scope
$user->assignRole($reviewer);             // throws RoleLimitExceeded
```

The cap is counted per scope, so a role in another project is unaffected. Re-assigning a role the model already holds is idempotent and never trips the cap, and the break-glass system role is exempt — it is neither counted nor blocked. Catch `RoleLimitExceeded` if you want to evict-then-assign instead of rejecting.

The system role (break-glass)
-----------------------------

[](#the-system-role-break-glass)

The `system` role implicitly holds everything and, with `scope_above_all` on, reaches every scope — intended for initial setup and emergency fixes, **not**routine use. Disable it afterward and it grants nothing:

```
$admin->assignRole($systemRole);
$admin->hasPermission('anything', $anyProject); // true while enabled

// .env: DELEGATED_PERMISSIONS_SYSTEM_ENABLED=false
$admin->hasPermission('anything', $anyProject); // false
```

Permission groups
-----------------

[](#permission-groups)

Bundle permissions and delegate them as a unit. A group can only be granted if the parent holds *every* permission in it (all-or-nothing); individual permissions can still be revoked afterward:

```
$permissions->createGroup('tags', ['manage-tags', 'delete-tags', 'create-tags']);

app(PermissionResolver::class)->grantGroup($adminRole, 'tags');
app(PermissionResolver::class)->revoke($adminRole, 'delete-tags'); // prunes just one
```

Managing the package itself
---------------------------

[](#managing-the-package-itself)

The CRUD operations are gated by the package's own permissions — see `Fanmade\DelegatedPermissions\ManagementPermission` (`create-roles`, `delete-permissions`, `assign-roles`, …). Seed them with `app(PermissionManager::class)->installManagementPermissions()`, grant them to an admin role, and gate your management UI with `$user->can('create-roles')`.

Testing
-------

[](#testing)

The suite runs on SQLite and PostgreSQL:

```
vendor/bin/pest                                   # SQLite (in-memory)
DB_CONNECTION=pgsql DB_DATABASE=testing \
  DB_USERNAME=postgres DB_PASSWORD=postgres \
  vendor/bin/pest                                 # PostgreSQL
```

License
-------

[](#license)

MIT. See [LICENSE](LICENSE).

###  Health Score

43

—

FairBetter than 89% of packages

Maintenance92

Actively maintained with recent releases

Popularity18

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity46

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

8

Last Release

38d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/03ee7b67b9a5fc7e28528dcd9756394b83f8923c6592fde5aaa43189d0022f7b?d=identicon)[Fanmade](/maintainers/Fanmade)

---

Top Contributors

[![Fanmade](https://avatars.githubusercontent.com/u/2896491?v=4)](https://github.com/Fanmade "Fanmade (24 commits)")

---

Tags

laravelauthorizationrolespermissionsrbacdelegation

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/fanmade-laravel-delegated-permissions/health.svg)

```
[![Health](https://phpackages.com/badges/fanmade-laravel-delegated-permissions/health.svg)](https://phpackages.com/packages/fanmade-laravel-delegated-permissions)
```

###  Alternatives

[laravel/ai

The official AI SDK for Laravel.

1.1k4.6M282](/packages/laravel-ai)[spatie/laravel-permission

Permission handling for Laravel 12 and up

13.0k107.5M1.6k](/packages/spatie-laravel-permission)[psalm/plugin-laravel

Psalm plugin for Laravel

3345.4M353](/packages/psalm-plugin-laravel)[illuminate/queue

The Illuminate Queue package.

20433.0M1.8k](/packages/illuminate-queue)[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

79227.1M206](/packages/laravel-mcp)[mike-bronner/laravel-model-caching

Automatic caching for Eloquent models.

2.4k161.4k1](/packages/mike-bronner-laravel-model-caching)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
