PHPackages                             carlopaa/access-control - 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. carlopaa/access-control

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

carlopaa/access-control
=======================

Tenant-aware access control with roles, groups, and permissions

v0.1.3(1mo ago)01↑2900%[1 PRs](https://github.com/carlopaa/access-control/pulls)MITPHPPHP ^8.3CI passing

Since Mar 26Pushed 1mo agoCompare

[ Source](https://github.com/carlopaa/access-control)[ Packagist](https://packagist.org/packages/carlopaa/access-control)[ Docs](https://github.com/carlopaa/access-control)[ GitHub Sponsors](https://github.com/carlopaa)[ RSS](/packages/carlopaa-access-control/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (13)Versions (3)Used By (0)

Access control for Laravel
==========================

[](#access-control-for-laravel)

[![Latest Version on Packagist](https://camo.githubusercontent.com/8ca9c58c2b82d172fa1e890c890ec5f9500624e8765e3a9b91cd1fc7c5e513ca/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6361726c6f7061612f6163636573732d636f6e74726f6c2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/carlopaa/access-control)[![Total Downloads](https://camo.githubusercontent.com/199f6ec9cbb1b1bb1db0d9f479bf75bb09bf6cb0d6af3b28a9bb5628a5d610df/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6361726c6f7061612f6163636573732d636f6e74726f6c2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/carlopaa/access-control)

`carlopaa/access-control` is a tenant-aware access control package for Laravel.

It combines role-based and permission-based patterns:

- users get roles in an organization context
- roles can map to default groups
- groups aggregate permissions
- users can also receive direct permissions
- `:deny` permissions override allows

Table of contents
-----------------

[](#table-of-contents)

- [Installation](#installation)
- [Quick start](#quick-start)
- [Required model setup](#required-model-setup)
- [Configuration reference](#configuration-reference)
- [Commands](#commands)
- [Using permissions and roles](#using-permissions-and-roles)
- [Role to default group sync](#role-to-default-group-sync)
- [Middleware](#middleware)
- [Gate integration](#gate-integration)
- [Tenant resolution](#tenant-resolution)
- [Testing](#testing)

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

[](#installation)

Install via Composer:

```
composer require carlopaa/access-control
```

Publish migrations and run them:

```
php artisan vendor:publish --tag="access-control-migrations"
php artisan migrate
```

Publish config:

```
php artisan vendor:publish --tag="access-control-config"
```

Quick start
-----------

[](#quick-start)

1. Add the trait to your user model:

```
use Aapolrac\AccessControl\Concerns\HasAccessControl;

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

2. Add a JSON `permissions` column to users (for direct permissions):

```
Schema::table('users', function (Blueprint $table) {
    $table->json('permissions')->nullable();
});
```

3. Configure `config/access_control.php` (models, enum classes, groups).
4. Seed permissions from your enums:

```
php artisan access-control:sync
```

5. Use checks in code:

```
$user->hasRole('owner');
$user->hasPermission('member:view-any');
```

Required model setup
--------------------

[](#required-model-setup)

The trait expects `roles()` and `groups()` relations on the user model.

```
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

public function roles(): BelongsToMany
{
    return $this->belongsToMany(
        Role::class,
        config('access_control.tables.role_user', 'role_user')
    )->withPivot('organization_id')->withTimestamps();
}

public function groups(): BelongsToMany
{
    return $this->belongsToMany(
        Group::class,
        config('access_control.tables.group_user', 'group_user')
    )->withPivot('organization_id')->withTimestamps();
}
```

Your `Group` model should have permissions relation:

```
public function permissions(): BelongsToMany
{
    return $this->belongsToMany(
        Permission::class,
        config('access_control.tables.group_permission', 'group_permission')
    )->withTimestamps();
}
```

Configuration reference
-----------------------

[](#configuration-reference)

`config/access_control.php`:

```
return [
    'models' => [
        'role' => Aapolrac\AccessControl\Models\Role::class,
        'group' => Aapolrac\AccessControl\Models\Group::class,
        'permission' => Aapolrac\AccessControl\Models\Permission::class,
    ],

    'tables' => [
        'roles' => 'roles',
        'groups' => 'groups',
        'permissions' => 'permissions',
        'role_user' => 'role_user',
        'group_user' => 'group_user',
        'group_permission' => 'group_permission',
    ],

    'context_cache' => [
        'enabled' => true,
        'key' => 'permissions',
    ],

    'permissions' => [
        'enum_classes' => [
            App\Enums\MemberPermission::class,
            App\Enums\CustomerPermission::class,
        ],
    ],

    'groups' => [
        // role key => default group keys
        'owner' => ['owners', 'team-management'],
        'manager' => ['team-management'],
    ],
];
```

By default, the package ships with these models:

- `Aapolrac\AccessControl\Models\Role`
- `Aapolrac\AccessControl\Models\Group`
- `Aapolrac\AccessControl\Models\Permission`

In your app, you can override them in `models` with your own Eloquent classes.

Example override:

```
'models' => [
    'role' => App\Models\Role::class,
    'group' => App\Models\Group::class,
    'permission' => App\Models\Permission::class,
],
```

Commands
--------

[](#commands)

Check package installation:

```
php artisan access-control
```

Sync permission records from configured enum classes:

```
php artisan access-control:sync
php artisan access-control:sync --only-missing
```

Using permissions and roles
---------------------------

[](#using-permissions-and-roles)

### Permission checks

[](#permission-checks)

```
$user->hasPermission('member:view-any');
$user->hasAnyPermission(['member:view-any', 'member:update']);
```

### Deny override

[](#deny-override)

If a user has `member:view-any:deny`, then `hasPermission('member:view-any')` returns `false` even if the allow exists from groups or direct permissions.

### Direct permission API

[](#direct-permission-api)

```
$user->assignPermission('customer:view-any');
$user->assignPermissions(['member:view-any', 'member:update']);
$user->revokePermission('member:update');
$user->syncDirectPermissions(['member:view-any']);
$user->clearDirectPermissions();
$direct = $user->getDirectPermissions(); // Collection
```

### Role checks

[](#role-checks)

```
$user->hasRole('owner');
$user->hasAnyRole(['owner', 'manager']);
$user->hasRoleInOrg('owner', $organizationId);
$user->hasAnyRoleInOrg(['owner', 'manager'], $organizationId);
```

### Query scopes

[](#query-scopes)

```
User::query()->withRole('owner')->get();
User::query()->withAnyRoles(['owner', 'manager'])->get();
User::query()->withRoleInOrg('owner', $organizationId)->get();
User::query()->withAnyRolesInOrg(['owner', 'manager'], $organizationId)->get();
```

Role to default group sync
--------------------------

[](#role-to-default-group-sync)

Use `RoleGroupSync` when role assignment should automatically maintain configured default groups:

```
use Aapolrac\AccessControl\Support\RoleGroupSync;

RoleGroupSync::syncDefaultsForRoles($user, $organizationId, ['owner', 'manager']);
```

You can also attach explicit groups by key or id:

```
RoleGroupSync::attach($user, $organizationId, ['team-management', 5]);
```

Troubleshooting
---------------

[](#troubleshooting)

### `vendor:publish --tag="access-control-config"` not available

[](#vendorpublish---tagaccess-control-config-not-available)

If the tag is not found, run:

```
composer update carlopaa/access-control -W
php artisan package:discover --ansi
php artisan vendor:publish --provider="Aapolrac\\AccessControl\\AccessControlServiceProvider" --tag="access-control-config"
```

If `config/access_control.php` already exists, Laravel will skip it. Use force when you want to overwrite:

```
php artisan vendor:publish --tag="access-control-config" --force
```

Middleware
----------

[](#middleware)

The package auto-registers middleware aliases:

- `access.permission`
- `access.role`

Usage:

```
Route::middleware(['auth', 'access.permission:member:view-any'])->group(function () {
    // ...
});

Route::middleware(['auth', 'access.role:owner,manager'])->group(function () {
    // ...
});
```

Gate integration
----------------

[](#gate-integration)

The package registers a `Gate::before` hook. If the user model has `hasPermission()`, then:

```
$user->can('member:view-any');
Gate::allows('member:view-any', $user);
```

Both resolve through package permissions.

Additionally, enum-based permission abilities can be auto-registered from `permissions.enum_classes`.

Tenant resolution
-----------------

[](#tenant-resolution)

If your checks/scopes need an implicit active organization, bind your own resolver:

```
use Aapolrac\AccessControl\Contracts\TenantResolver;

app()->bind(TenantResolver::class, YourTenantResolver::class);
```

Your resolver must return the current organization id (or `null`):

```
public function resolveOrganizationId(?Model $tenant = null): ?int
{
    return $tenant?->getKey() ? (int) $tenant->getKey() : null;
}
```

Testing
-------

[](#testing)

Run package tests:

```
composer test
```

Run static analysis:

```
composer analyse
```

Credits
-------

[](#credits)

- [Carlo Garcia Paa](https://github.com/carlopaa)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

37

—

LowBetter than 83% of packages

Maintenance91

Actively maintained with recent releases

Popularity2

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity40

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 85.7% 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

Unknown

Total

1

Last Release

46d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/f4440f2b2f20e0792bd105640e7a6c7d470ad30874eee917ffc0703e101cc549?d=identicon)[carlopaa](/maintainers/carlopaa)

---

Top Contributors

[![carlopaa](https://avatars.githubusercontent.com/u/11911741?v=4)](https://github.com/carlopaa "carlopaa (6 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (1 commits)")

---

Tags

laravelaclrbacaccess-controlcarlopaa

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/carlopaa-access-control/health.svg)

```
[![Health](https://phpackages.com/badges/carlopaa-access-control/health.svg)](https://phpackages.com/packages/carlopaa-access-control)
```

###  Alternatives

[spatie/laravel-permission

Permission handling for Laravel 12 and up

12.9k89.8M1.0k](/packages/spatie-laravel-permission)[bezhansalleh/filament-shield

Filament support for `spatie/laravel-permission`.

2.8k2.9M88](/packages/bezhansalleh-filament-shield)[casbin/laravel-authz

An authorization library that supports access control models like ACL, RBAC, ABAC in Laravel.

324339.9k4](/packages/casbin-laravel-authz)[binary-cats/laravel-rbac

Laravel enum-backed RBAC extension of spatie/laravel-permission

7730.4k](/packages/binary-cats-laravel-rbac)[erag/laravel-role-permission

A simple and easy-to-install role and permission management package for Laravel, supporting versions 10.x and 11.x

404.2k](/packages/erag-laravel-role-permission)[hosseinhezami/laravel-permission-manager

Advanced permission manager for Laravel.

403.3k](/packages/hosseinhezami-laravel-permission-manager)

PHPackages © 2026

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