silber/bouncer
Bouncer adds roles and abilities to Laravel with a fluent, Eloquent-powered API. Define permissions, assign roles to users, and authorize actions via gates and middleware. Supports caching, scoped abilities, and a simple, expressive permission model.
## Getting Started
### Minimal Setup
1. **Installation**:
```bash
composer require silber/bouncer
Publish the migration and config:
php artisan vendor:publish --provider="Bouncer\BouncerServiceProvider"
php artisan migrate
First Use Case: Define a role and assign it to a user:
use Bouncer;
// Create a role
$adminRole = Bouncer::role()->create(['name' => 'admin']);
// Assign the role to a user
$user = User::find(1);
$user->assign($adminRole);
// Grant an ability to the role
$adminRole->allowTo('view', Post::class);
Check Permissions:
if (Bouncer::can($user, 'view', Post::class)) {
// User can view posts
}
config/bouncer.php: Configuration for caching, models, and behavior.database/migrations/..._create_bouncer_tables.php: Understand the schema (roles, abilities, and their relationships).app/Providers/BouncerServiceProvider.php: Service provider for customizations.$adminRole = Bouncer::role()->create(['name' => 'admin']);
$editorRole = Bouncer::role()->create(['name' => 'editor']);
$user->assign($adminRole);
$user->assign($editorRole);
$adminRole->allowTo('delete', Post::class);
$editorRole->allowTo('update', Post::class);
Bouncer::allow($user)->to('publish', Post::class);
Bouncer::forbid($user)->to('publish', Post::class);
Bouncer::allowEveryone()->to('view', Post::class);
Bouncer::scope()->to($tenantId, function () {
$tenantAdmin = Bouncer::role()->create(['name' => 'tenant_admin']);
$tenantAdmin->allowTo('manage', TenantPost::class);
});
Bouncer::scope()->onceTo($otherTenantId, function () {
// Queries here use $otherTenantId
});
// No config needed; Bouncer runs after policies by default.
// To revert (e.g., for legacy code):
Bouncer::runBeforePolicies();
class PostPolicy {
public function delete(User $user, Post $post) {
return $post->isPublished(); // Policy overrides Bouncer if false
}
}
canAny() to check if a user has any of multiple abilities:
if (Bouncer::canAny($user, ['view', 'update'], Post::class)) {
// User can view or update
}
public function handle(Request $request, Closure $next) {
if (!Bouncer::can($request->user(), 'edit', Post::class)) {
abort(403);
}
return $next($request);
}
Route::middleware(['auth', 'can:edit,post'])->group(function () {
// Protected routes
});
public function update(Request $request, Post $post) {
$this->authorize('update', $post); // Laravel policy
if (!Bouncer::can($request->user(), 'update', Post::class)) {
return response()->json(['error' => 'Unauthorized'], 403);
}
// Proceed
}
DatabaseSeeder:
public function run() {
$adminRole = Bouncer::role()->create(['name' => 'admin']);
$adminRole->allowTo(['view', 'create', 'update', 'delete'], Post::class);
$adminRole->allowTo(['manage'], User::class);
$user = User::first();
$user->assign($adminRole);
}
class CustomRole extends \Bouncer\Role {
use \Illuminate\Database\Eloquent\SoftDeletes;
}
Bouncer::useRoleModel(CustomRole::class);
file):
'cache' => [
'driver' => 'redis', // or 'file', 'database'
'ttl' => 60, // Cache TTL in minutes
],
Bouncer::refresh();
Bouncer::scope()->to($tenantId, ...) to explicitly scope operations. Avoid mixing global and scoped grants.bouncer_permissions table for unexpected entity_id/entity_type values.Bouncer::runBeforePolicies() if you need Bouncer to override policies.dd(Bouncer::can($user, 'action', Model::class)) to see evaluation order.Bouncer::role()->where(...)->withTrashed()->get() or explicitly restore:
$role = Bouncer::role()->findOrFail($id)->restore();
Bouncer::role()->withTrashed()->get() to inspect soft-deleted roles.entity_type mismatches.\Illuminate\Database\Eloquent\Model::morphMap([
'custom_role' => \App\Models\CustomRole::class,
]);
entity_type in bouncer_permissions for unexpected values.Bouncer::refresh();
'cache' => ['driver' => null]) to test.allowEveryone() may unintentionally override scoped abilities.allowEveryone() sparingly for truly global actions (e.g., "view public posts").Bouncer::runBeforePolicies() or Bouncer::runAfterPolicies() explicitly. Document which system handles which checks.config/bouncer.php:
'tables' => [
'permissions' => 'bouncer_permissions',
'roles' => 'bouncer_roles',
],
How can I help you explore Laravel packages today?